import express from "express"
import { ToMain } from "../../../types/IPC/ToMain"
import { getContentProviderAccess, setContentProviderAccess } from "../../data/contentProviders"
import { sendToMain } from "../../IPC/main"
import { getKey } from "../../utils/keys"
import { OAuth2Helper } from "../base/OAuth2Helper"
import type { AmazingLifeAuthData, AmazingLifeScopes } from "./types"
import { AMAZING_LIFE_OAUTH_BASE } from "./types"

/**
 * Handles authentication and connection management with the AmazingLife (APlay) service.
 * Manages OAuth PKCE flow, token refresh, and connection state.
 *
 * WARNING: This class should ONLY be accessed through AmazingLifeProvider.
 * Do not import or use this class directly in other parts of the application.
 * Use ContentProviderRegistry or AmazingLifeProvider instead.
 */
export class AmazingLifeConnect {
    private static readonly AMAZING_LIFE_PORT = 5502
    private static AMAZING_LIFE_ACCESS: AmazingLifeAuthData | null = null
    private static readonly clientId: string = getKey("amazinglife_id") || ""
    private static oauthHelper: OAuth2Helper<AmazingLifeAuthData>
    private static app = express()
    private static routeSetup = false

    private static initializeOAuthHelper(): void {
        if (!this.oauthHelper) {
            const redirectUri = `http://localhost:${this.AMAZING_LIFE_PORT}/auth/complete`
            this.oauthHelper = new OAuth2Helper<AmazingLifeAuthData>({
                clientId: this.clientId,
                clientSecret: "", // Not needed for PKCE flow
                authUrl: `${AMAZING_LIFE_OAUTH_BASE}/authorize`,
                tokenUrl: `${AMAZING_LIFE_OAUTH_BASE}/token`,
                redirectUri,
                scopes: ["openid", "profile", "email"],
                usePKCE: true,
                additionalParams: { state: "xyz" }
            })
        }

        // Set up the auth callback route
        if (!this.routeSetup) {
            this.app.get("/auth/complete", (req, res) => {
                this.handleAuthCallback(req, res)
            })
            this.routeSetup = true
        }
    }

    public static initialize() {
        this.AMAZING_LIFE_ACCESS = null
    }

    public static async connect(scope: AmazingLifeScopes): Promise<AmazingLifeAuthData | null> {
        let accessData = this.AMAZING_LIFE_ACCESS || (getContentProviderAccess("amazinglife", scope) as AmazingLifeAuthData | null)

        if (this.isTokenExpired(accessData)) accessData = await this.refreshToken(scope, accessData)
        if (!accessData) accessData = await this.authenticate(scope)
        if (!accessData) return null

        if (!this.AMAZING_LIFE_ACCESS) connectionInitialized()
        this.AMAZING_LIFE_ACCESS = accessData

        return accessData
    }

    public static disconnect(scope: AmazingLifeScopes = "openid profile email"): void {
        console.log(`APlay: Disconnecting (scope: ${scope})`)
        setContentProviderAccess("amazinglife", scope, null)
        this.AMAZING_LIFE_ACCESS = null
    }

    public static getAccessToken(): string | null {
        return this.AMAZING_LIFE_ACCESS?.access_token || null
    }

    public static async ensureValidToken(scope: AmazingLifeScopes = "openid profile email"): Promise<string | null> {
        let accessData = this.AMAZING_LIFE_ACCESS || (getContentProviderAccess("amazinglife", scope) as AmazingLifeAuthData | null)

        if (!accessData) {
            console.warn("No active session. Please connect first.")
            return null
        }

        if (this.isTokenExpired(accessData)) {
            accessData = await this.refreshToken(scope, accessData)
            if (!accessData) {
                console.error("Failed to refresh token")
                return null
            }
            this.AMAZING_LIFE_ACCESS = accessData
        }

        return accessData.access_token
    }

    private static isTokenExpired(access: AmazingLifeAuthData | null): boolean {
        if (!access) return true

        try {
            // Decode JWT token to get expiration time
            const tokenParts = access?.access_token.split(".")
            if (!tokenParts || tokenParts.length !== 3) {
                console.warn("Invalid JWT token format")
                return true
            }

            // Decode the payload (second part of JWT)
            const payload = JSON.parse(Buffer.from(tokenParts[1], "base64").toString("utf-8"))
            // console.log("payload---------------", payload)

            if (!payload.exp) {
                console.warn("No exp claim found in JWT token")
                return true
            }

            // exp is in seconds, Date.now() is in milliseconds
            const isExpired = payload.exp * 1000 < Date.now()
            // console.log(`APlay: Token ${isExpired ? "expired" : "valid"} (exp: ${new Date(payload.exp * 1000).toISOString()})`)

            return isExpired
        } catch (error) {
            console.error("Failed to decode JWT token:", error)
            return true
        }
    }

    private static async refreshToken(scope: AmazingLifeScopes, existingAccess?: AmazingLifeAuthData | null): Promise<AmazingLifeAuthData | null> {
        const currentAccess = existingAccess || this.AMAZING_LIFE_ACCESS
        if (!currentAccess?.refresh_token) return null

        try {
            this.initializeOAuthHelper()
            const refreshed = await this.oauthHelper.refreshAccessToken(currentAccess.refresh_token, scope)
            if (refreshed) {
                setContentProviderAccess("amazinglife", scope, refreshed)
            }
            return refreshed
        } catch (error) {
            console.error("Failed to refresh APlay token:", error)
            return null
        }
    }

    private static async authenticate(scope: AmazingLifeScopes): Promise<AmazingLifeAuthData | null> {
        this.initializeOAuthHelper()

        const server = this.app.listen(this.AMAZING_LIFE_PORT, () => {
            console.info(`Listening for APlay OAuth response at port ${this.AMAZING_LIFE_PORT}`)
        })

        server.once("error", (err: Error) => {
            if ((err as any).code === "EADDRINUSE") server.close()
        })

        try {
            const authData = await this.oauthHelper.authorize(scope)
            if (!authData) return null

            setContentProviderAccess("amazinglife", scope, authData)
            this.AMAZING_LIFE_ACCESS = authData
            connectionInitialized(true)
            return authData
        } catch (error) {
            console.error("APlay authentication failed:", error)
            return null
        } finally {
            server.close()
        }
    }

    public static handleAuthCallback(req: express.Request, res: express.Response): void {
        this.initializeOAuthHelper()
        this.oauthHelper.handleCallback(req, res)
    }
}

function connectionInitialized(isFirstConnection = false): void {
    sendToMain(ToMain.PROVIDER_CONNECT, { providerId: "amazinglife", success: true, isFirstConnection })
}
