import XCTest
@testable import WMF

class URLParsingAndRoutingTests: XCTestCase {
    let configuration = Configuration.current
    let router = Configuration.current.router
    
    func testWikiResourcePath() {
        XCTAssertEqual("/wiki//æ/_raising".wikiResourcePath, "/æ/_raising")
        XCTAssertNil("/w//æ/_raising".wikiResourcePath)
        XCTAssertEqual("/wiki/อักษรละติน".wikiResourcePath, "อักษรละติน")
    }
    
    func testWResourcePath() {
        XCTAssertEqual("/w/index.php?title=/æ/_raising&oldid=905679984".wResourcePath, "index.php?title=/æ/_raising&oldid=905679984")
    }
    
    func testNamespace() {
        XCTAssertEqual("The_Clash:_Westway_to_the_World".namespaceOfWikiResourcePath(with: "en"), .main)
        XCTAssertEqual("The_Clash:_Westway_to_the_World".namespaceAndTitleOfWikiResourcePath(with: "en").title,  "The_Clash:_Westway_to_the_World")
    }
    
    func testMainPage() {
        let enMainPageURL = URL(string: "https://en.m.wikipedia.org/wiki/Main_Page")!
        let dest = router.destination(for: enMainPageURL, loggedInUsername: nil)
        switch dest {
        case .inAppLink(let linkURL):
            XCTAssertEqual(linkURL, enMainPageURL.canonical)
        default:
            XCTAssertTrue(false)
        }
    }
    
    func testSpecialDestinations() {
        let myTalkURL = URL(string: "https://en.wikipedia.org/wiki/Special:MyTalk")!
        let myTalkDest = router.destination(for: myTalkURL, loggedInUsername: "Jimbo Wales")
        
        switch myTalkDest {
        case .userTalk:
            XCTAssertTrue(true)
        default:
            XCTAssertTrue(false)
        }
        
        let myContributionsURL = URL(string: "https://en.wikipedia.org/wiki/Special:MyContributions")!
        let myContributionsDest = router.destination(for: myContributionsURL, loggedInUsername: "Jimbo Wales")
        
        switch myContributionsDest {
        case .inAppLink:
            XCTAssertTrue(true)
        default:
            XCTAssertTrue(false)
        }
        
        let userLoginURL1 = URL(string: "https://en.wikipedia.org/wiki/Special:UserLogin")!
        let userLoginDest1 = router.destination(for: userLoginURL1, loggedInUsername: nil)
        
        switch userLoginDest1 {
        case .login:
            XCTAssertTrue(true)
        default:
            XCTAssertTrue(false)
        }
        
        let userLoginURL2 = URL(string: "https://en.wikipedia.org/w/index.php?title=Special:UserLogin")!
        let userLoginDest2 = router.destination(for: userLoginURL2, loggedInUsername: nil)
        
        switch userLoginDest2 {
        case .login:
            XCTAssertTrue(true)
        default:
            XCTAssertTrue(false)
        }
        
        let importReadingListsURL = URL(string: "https://en.wikipedia.org/wiki/Special:ReadingLists?limport=eyJsaXN0Ijp7ImVuIjpbMjE4NjksMzI3NDUsNDQ0NjksNDQ0NzQsODI3ODBdfX0=")!
        let importReadingListsDest = router.destination(for: importReadingListsURL, loggedInUsername: nil)
        
        switch importReadingListsDest {
        case .readingListsImport:
            XCTAssertTrue(true)
        default:
            XCTAssertTrue(false)
        }
    }
    
    func testWikiResourcePathActivity() {
        guard var components = URLComponents(string: "//en.wikipedia.org/wiki/User_talk:Pink_Bull") else {
            XCTAssertTrue(false)
            return
        }
        
        var dest = router.destination(for: components.url!, loggedInUsername: nil)
        switch dest {
        case .userTalk(let linkURL):
            XCTAssertEqual(linkURL, components.url!.canonical)
        default:
            XCTAssertTrue(false)
        }

        components.path = "/wiki//æ/_raising"
        dest = router.destination(for: components.url!, loggedInUsername: nil)
        switch dest {
        case .article(let linkURL):
            XCTAssertEqual(linkURL, components.url!.canonical)
        default:
            XCTAssertTrue(false)
        }
        
        components.host = "fr.m.wikipedia.org"
        components.path = "/wiki/France"
        dest = router.destination(for: components.url!, loggedInUsername: nil)
        switch dest {
        case .article(let linkURL):
            XCTAssertEqual(linkURL, components.url!.canonical)
        default:
            XCTAssertTrue(false)
        }
        
        // Special should work on frwiki because it's a canonical namespace
        components.path = "/wiki/Special:MobileDiff/24601"
        dest = router.destination(for: components.url!, loggedInUsername: nil)
        switch dest {
        case .articleDiffSingle(let linkURL, _, let toRevID):
            XCTAssertEqual(linkURL, components.url!.canonical)
            XCTAssertEqual(toRevID, 24601)
        default:
            XCTAssertTrue(false)
        }
        
        components.host = "zh.m.wikipedia.org"
        components.path = "/wiki/特殊:MobileDiff/24601"
        dest = router.destination(for: components.url!, loggedInUsername: nil)
        switch dest {
        case .articleDiffSingle(let linkURL, _, let toRevID):
            XCTAssertEqual(linkURL, components.url!.canonical)
            XCTAssertEqual(toRevID, 24601)
        default:
            XCTAssertTrue(false)
        }
    }
    
    func testTitlesWithForwardSlashes() {
        var url = URL(string: "https://en.wikipedia.org/wiki/G/O_Media")!
        XCTAssertEqual(url.resolvingRelativeWikiHref("./Gizmodo")?.absoluteString, "https://en.wikipedia.org/wiki/Gizmodo")
        url = URL(string: "https://en.wikipedia.org/wiki//dev/random")!
        XCTAssertEqual(url.resolvingRelativeWikiHref(".//dev/null")?.absoluteString, "https://en.wikipedia.org/wiki//dev/null")
    }
    
    /// Ensure the 'transcoded' path component is inserted after the language path component for audio hosted on Wikipedia
    func testWikipediaCompatibilityAdjustments() {
        let url = URL(string: "https://upload.wikimedia.org/wikipedia/en/3/3f/DeanScream.ogg")!
        let expected = URL(string: "https://upload.wikimedia.org/wikipedia/en/transcoded/3/3f/DeanScream.ogg/DeanScream.ogg.mp3")!
        XCTAssertEqual(url.byMakingAudioFileCompatibilityAdjustments, expected)
    }
    
    /// Ensure the 'transcoded' path component is inserted after the 'commons' path component for audio hosted on Commons
    func testCommonsCompatibilityAdjustments() {
        let url = URL(string: "https://upload.wikimedia.org/wikipedia/commons/8/8a/En-Paprika_%28American%29.oga")!
        let expected = URL(string: "https://upload.wikimedia.org/wikipedia/commons/transcoded/8/8a/En-Paprika_(American).oga/En-Paprika_(American).oga.mp3")!
        XCTAssertEqual(url.byMakingAudioFileCompatibilityAdjustments, expected)
    }
    
    /// Ensure non audio and non-upload.wikimedia.org links aren't transcoded and don't break in unexpected ways
    func testInvalidCompatibilityAdjustment() {
        var url = URL(string: "https://upload.wikimedia.org/commons/3/3f/DeanScream.ogv")!
        XCTAssertFalse(url.isWikimediaHostedAudioFileLink)
        url = URL(string: "https://en.wikipedia.org/commons/3/3f/DeanScream.ogg")!
        XCTAssertFalse(url.isWikimediaHostedAudioFileLink)
    }

    func testSpecialCharactersEncodedOnWiki() {
        let url = URL(string: "https://de.wikipedia.org/wiki/Grinnell_College")!
        XCTAssertEqual(url.resolvingRelativeWikiHref("./COVID-19-Pandemie_in_Baden-W%C3%BCrttemberg")?.absoluteString, "https://de.wikipedia.org/wiki/COVID-19-Pandemie_in_Baden-W%C3%BCrttemberg")
    }

    func testSpecialCharactersUnencodedOnWiki() {
        let url = URL(string: "https://de.wikipedia.org/wiki/Grinnell_College")!
        XCTAssertEqual(url.resolvingRelativeWikiHref("./COVID-19-Pandemie_in_Baden-Württemberg")?.absoluteString, "https://de.wikipedia.org/wiki/COVID-19-Pandemie_in_Baden-W%C3%BCrttemberg")
    }

    func testSpecialCharactersEncodedOnAnotherWiki() {
        let url = URL(string: "https://en.wikipedia.org/wiki/Grinnell_College")!
        XCTAssertEqual(url.resolvingRelativeWikiHref("//de.wikipedia.org/wiki/COVID-19-Pandemie_in_Baden-W%C3%BCrttemberg")?.absoluteString, "https://de.wikipedia.org/wiki/COVID-19-Pandemie_in_Baden-W%C3%BCrttemberg")
    }

    func testSpecialCharactersUnencodedOnAnotherWiki() {
        let url = URL(string: "https://en.wikipedia.org/wiki/Grinnell_College")!
        XCTAssertEqual(url.resolvingRelativeWikiHref("//de.wikipedia.org/wiki/COVID-19-Pandemie_in_Baden-Württemberg")?.absoluteString, "https://de.wikipedia.org/wiki/COVID-19-Pandemie_in_Baden-W%C3%BCrttemberg")
    }

    func testSpecialCharactersEncodedOnCommons() {
        let url = URL(string: "https://de.wikipedia.org/wiki/Grinnell_College")!
        XCTAssertEqual(url.resolvingRelativeWikiHref("//commons.wikimedia.org/wiki/Category:COVID-19_pandemic_in_Baden-W%C3%BCrttemberg")?.absoluteString, "https://commons.wikimedia.org/wiki/Category:COVID-19_pandemic_in_Baden-W%C3%BCrttemberg")
    }

    func testSpecialCharactersUnencodedOnCommons() {
        let url = URL(string: "https://de.wikipedia.org/wiki/Grinnell_College")!
        XCTAssertEqual(url.resolvingRelativeWikiHref("//commons.wikimedia.org/wiki/Category:COVID-19_pandemic_in_Baden-Württemberg")?.absoluteString, "https://commons.wikimedia.org/wiki/Category:COVID-19_pandemic_in_Baden-W%C3%BCrttemberg")
    }

    func testSpecialCharactersEncodedOffWiki() {
        let url = URL(string: "https://de.wikipedia.org/wiki/Grinnell_College")!
        XCTAssertEqual(url.resolvingRelativeWikiHref("//www.p%C3%BCzzledpint.com")?.absoluteString, "https://www.p%C3%BCzzledpint.com")
    }

    func testSpecialCharactersUnencodedOffWiki() {
        let url = URL(string: "https://de.wikipedia.org/wiki/Grinnell_College")!
        XCTAssertEqual(url.resolvingRelativeWikiHref("//www.püzzledpint.com")?.absoluteString, "https://www.p%C3%BCzzledpint.com")
    }

    func testQuestionMark() {
        let url = URL(string: "https://de.wikipedia.org/wiki/Grinnell_College")!
        XCTAssertEqual(url.resolvingRelativeWikiHref("//www.puzzledpint.com/page?test=yes")?.absoluteString, "https://www.puzzledpint.com/page?test=yes")
    }

    func testSpecialCharactersEncodedWithQuestionMark() {
        let url = URL(string: "https://de.wikipedia.org/wiki/Grinnell_College")!
        XCTAssertEqual(url.resolvingRelativeWikiHref("//commons.wikimedia.org/wiki/Category:COVID-19_pandemic_in_Baden-W%C3%BCrttemberg?uselang=de")?.absoluteString, "https://commons.wikimedia.org/wiki/Category:COVID-19_pandemic_in_Baden-W%C3%BCrttemberg?uselang=de")
    }

    func testSpecialCharactersUnencodedWithQuestionMark() {
        let url = URL(string: "https://de.wikipedia.org/wiki/Grinnell_College")!
        XCTAssertEqual(url.resolvingRelativeWikiHref("//commons.wikimedia.org/wiki/Category:COVID-19_pandemic_in_Baden-Württemberg?uselang=de")?.absoluteString, "https://commons.wikimedia.org/wiki/Category:COVID-19_pandemic_in_Baden-W%C3%BCrttemberg?uselang=de")
    }
    
    func testQuestionMarkInPath() {
        let url = URL(string: "https://en.wikipedia.org/wiki/Main_Page")!
        XCTAssertEqual(url.resolvingRelativeWikiHref("./Who's_the_Man%3F")?.absoluteString, "https://en.wikipedia.org/wiki/Who's_the_Man%3F")
    }
    
    func testCaret() {
        let url = URL(string: "https://en.wikipedia.org/wiki/Main_Page")!
        XCTAssertEqual(url.resolvingRelativeWikiHref("//commons.wikimedia.org/wiki/File:^8_Schickelgruber._He_was_sick_two_days_last_week_-_NARA_-_535224.jpg")?.absoluteString, "https://commons.wikimedia.org/wiki/File:%5E8_Schickelgruber._He_was_sick_two_days_last_week_-_NARA_-_535224.jpg")
    }
    
    func testLanguageVariantCodeProperty() {
        var url = URL(string: "https://zh.wikipedia.org")!
        XCTAssertNil(url.wmf_languageVariantCode)
        let languageVariantCode = "zh-hant"
        url.wmf_languageVariantCode = languageVariantCode
        XCTAssertEqual(url.wmf_languageVariantCode, languageVariantCode)

        // Assignment of value type URL preserves the language variant code associated object
        // Both are backed by the same copy-on-write NSURL instance
        let url2 = url
        XCTAssertEqual(url2.wmf_languageVariantCode, languageVariantCode)

        // Creating new URL instances based on the original DOES NOT automatically
        // propagate the language variant code associated object
        let url3 = url.appendingPathComponent("test")
        XCTAssertNotEqual(url3.wmf_languageVariantCode, languageVariantCode)
    }
    
    func testContentLanguageCodeProperty() {
        let languageCode = "zh"
        let languageVariantCode = "zh-hans"
        var url = URL(string: "https://\(languageCode).wikipedia.org")!

        // If languageVariantCode is non-nil and non-empty string, wmf_contentLanguageCode returns languageVariantCode
        url.wmf_languageVariantCode = languageVariantCode
        XCTAssertEqual(url.wmf_contentLanguageCode, languageVariantCode)
        
        // If languageVariantCode is nil, contentLanguageCode returns languageCode
        url.wmf_languageVariantCode = nil
        XCTAssertEqual(url.wmf_contentLanguageCode, languageCode)
        
        // If languageVariantCode is an empty string, contentLanguageCode returns languageCode
        url.wmf_languageVariantCode = ""
        XCTAssertEqual(url.wmf_contentLanguageCode, languageCode)
    }
    
    func testLanguageVariantCodePropertyFromURLComponents() {
        let components = URLComponents(string: "https://sr.wikipedia.org")
        let languageVariantCode = "sr-ec"
        XCTAssertNotNil(components)
        if let components = components {
            let url = components.wmf_URLWithLanguageVariantCode(languageVariantCode)
            XCTAssertNotNil(url)
            XCTAssertEqual(url?.wmf_languageVariantCode, languageVariantCode)
        }
    }

}
