import Foundation
import Siesta
import SwiftUI

final class AccountValidator: Service {
    let app: Binding<VideosApp>
    let url: String
    let account: Account!

    var formObjectID: Binding<String>
    var isValid: Binding<Bool>
    var isValidated: Binding<Bool>
    var isValidating: Binding<Bool>
    var error: Binding<String?>?

    init(
        app: Binding<VideosApp>,
        url: String,
        account: Account? = nil,
        id: Binding<String>,
        isValid: Binding<Bool>,
        isValidated: Binding<Bool>,
        isValidating: Binding<Bool>,
        error: Binding<String?>? = nil
    ) {
        self.app = app
        self.url = url
        self.account = account
        formObjectID = id
        self.isValid = isValid
        self.isValidated = isValidated
        self.isValidating = isValidating
        self.error = error

        super.init(baseURL: url)
        configure()
    }

    func configure() {
        configure {
            $0.pipeline[.parsing].add(SwiftyJSONTransformer, contentTypes: ["*/json"])
        }

        configure("/api/v1/auth/feed", requestMethods: [.get]) {
            guard self.account != nil else {
                return
            }

            $0.headers["Cookie"] = self.invidiousCookieHeader
        }

        configure("/login", requestMethods: [.post]) {
            $0.headers["Content-Type"] = "application/json"
        }
    }

    func validateInstance() {
        reset()

        neverGonnaGiveYouUp
            .load()
            .onSuccess { response in
                guard self.url == self.formObjectID.wrappedValue else {
                    return
                }

                let json = response.json.dictionaryValue
                let author = self.app.wrappedValue == .invidious ? json["author"] : json["uploader"]

                if author == "Rick Astley" {
                    self.isValid.wrappedValue = true
                    self.error?.wrappedValue = nil
                } else {
                    self.isValid.wrappedValue = false
                }
            }
            .onFailure { error in
                guard self.url == self.formObjectID.wrappedValue else {
                    return
                }

                self.isValid.wrappedValue = false
                self.error?.wrappedValue = error.userMessage
            }
            .onCompletion { _ in
                self.isValidated.wrappedValue = true
                self.isValidating.wrappedValue = false
            }
    }

    func validateAccount() {
        reset()

        accountRequest
            .onSuccess { response in
                guard self.account!.username == self.formObjectID.wrappedValue else {
                    return
                }

                switch self.app.wrappedValue {
                case .invidious:
                    self.isValid.wrappedValue = true
                case .piped:
                    let error = response.json.dictionaryValue["error"]?.string
                    let token = response.json.dictionaryValue["token"]?.string
                    self.isValid.wrappedValue = error?.isEmpty ?? !(token?.isEmpty ?? true)
                    self.error!.wrappedValue = error
                }
            }
            .onFailure { _ in
                guard self.account!.username == self.formObjectID.wrappedValue else {
                    return
                }

                self.isValid.wrappedValue = false
            }
            .onCompletion { _ in
                self.isValidated.wrappedValue = true
                self.isValidating.wrappedValue = false
            }
    }

    var accountRequest: Request {
        switch app.wrappedValue {
        case .invidious:
            return feed.load()
        case .piped:
            return login.request(.post, json: ["username": account.username, "password": account.password])
        }
    }

    func reset() {
        isValid.wrappedValue = false
        isValidated.wrappedValue = false
        isValidating.wrappedValue = false
        error?.wrappedValue = nil
    }

    var invidiousCookieHeader: String {
        "SID=\(account.username)"
    }

    var login: Resource {
        resource("/login")
    }

    var feed: Resource {
        resource("/api/v1/auth/feed")
    }

    var videoResourceBasePath: String {
        app.wrappedValue == .invidious ? "/api/v1/videos" : "/streams"
    }

    var neverGonnaGiveYouUp: Resource {
        resource("\(videoResourceBasePath)/dQw4w9WgXcQ")
    }
}