import Foundation
import OpenClawProtocol

func whatsappLoginWaitRequestTimeoutMs(
    startedAt: Date,
    timeoutMs: Int,
    didRunFinalWait: inout Bool,
    now: Date = Date()) -> Int?
{
    let elapsedMs = Int(now.timeIntervalSince(startedAt) * 1000)
    let remainingMs = max(timeoutMs - elapsedMs, 0)
    if remainingMs > 0 {
        return remainingMs
    }
    if didRunFinalWait {
        return nil
    }
    didRunFinalWait = true
    return 1
}

extension ChannelsStore {
    func start() {
        guard !self.isPreview else { return }
        self.startCount += 1
        guard self.startCount == 1 else { return }
        guard self.pollTask == nil else { return }
        self.pollTask = Task.detached { [weak self] in
            guard let self else { return }
            await self.refresh(probe: false)
            async let schemaLoad: Void = self.loadConfigSchema()
            async let configLoad: Void = self.loadConfig(force: false)
            _ = await (schemaLoad, configLoad)
            while !Task.isCancelled {
                try? await Task.sleep(nanoseconds: UInt64(self.interval * 1_000_000_000))
                await self.refresh(probe: false)
            }
        }
    }

    func stop() {
        guard !self.isPreview else { return }
        guard self.startCount > 0 else { return }
        self.startCount -= 1
        guard self.startCount == 0 else { return }
        self.pollTask?.cancel()
        self.pollTask = nil
    }

    func refresh(probe: Bool) async {
        guard !self.isRefreshing else { return }
        self.isRefreshing = true
        defer { self.isRefreshing = false }

        do {
            let statusTimeoutMs = probe ? 8000 : 2500
            let params: [String: AnyCodable] = [
                "probe": AnyCodable(probe),
                "timeoutMs": AnyCodable(statusTimeoutMs),
            ]
            let snap: ChannelsStatusSnapshot = try await GatewayConnection.shared.requestDecoded(
                method: .channelsStatus,
                params: params,
                timeoutMs: probe ? 12000 : 5000)
            self.snapshot = snap
            self.lastSuccess = Date()
            self.lastError = nil
        } catch {
            self.lastError = error.localizedDescription
        }
    }

    func startWhatsAppLogin(force: Bool, autoWait: Bool = true) async {
        guard !self.whatsappBusy else { return }
        self.whatsappBusy = true
        defer { self.whatsappBusy = false }
        var shouldAutoWait = false
        do {
            let params: [String: AnyCodable] = [
                "force": AnyCodable(force),
                "timeoutMs": AnyCodable(30000),
            ]
            let result: WhatsAppLoginStartResult = try await GatewayConnection.shared.requestDecoded(
                method: .webLoginStart,
                params: params,
                timeoutMs: 35000)
            self.whatsappLoginMessage = result.message
            self.whatsappLoginQrDataUrl = result.qrDataUrl
            self.whatsappLoginConnected = result.connected
            shouldAutoWait = autoWait && result.qrDataUrl != nil
        } catch {
            self.whatsappLoginMessage = error.localizedDescription
            self.whatsappLoginQrDataUrl = nil
            self.whatsappLoginConnected = nil
        }
        await self.refresh(probe: true)
        if shouldAutoWait {
            Task { await self.waitWhatsAppLogin() }
        }
    }

    func waitWhatsAppLogin(timeoutMs: Int = 120_000) async {
        guard !self.whatsappBusy else { return }
        self.whatsappBusy = true
        defer { self.whatsappBusy = false }
        let startedAt = Date()
        var didRunFinalWait = false
        do {
            while let remainingMs = whatsappLoginWaitRequestTimeoutMs(
                startedAt: startedAt,
                timeoutMs: timeoutMs,
                didRunFinalWait: &didRunFinalWait)
            {
                var params: [String: AnyCodable] = [
                    "timeoutMs": AnyCodable(remainingMs),
                ]
                if let currentQrDataUrl = self.whatsappLoginQrDataUrl {
                    params["currentQrDataUrl"] = AnyCodable(currentQrDataUrl)
                }
                let result: WhatsAppLoginWaitResult = try await GatewayConnection.shared.requestDecoded(
                    method: .webLoginWait,
                    params: params,
                    timeoutMs: Double(remainingMs) + 5000)
                self.applyWhatsAppLoginWaitResult(result)
                if result.connected || result.qrDataUrl == nil || didRunFinalWait {
                    break
                }
            }
        } catch {
            self.whatsappLoginMessage = error.localizedDescription
        }
        await self.refresh(probe: true)
    }

    func logoutWhatsApp() async {
        guard !self.whatsappBusy else { return }
        self.whatsappBusy = true
        defer { self.whatsappBusy = false }
        do {
            let params: [String: AnyCodable] = [
                "channel": AnyCodable("whatsapp"),
            ]
            let result: ChannelLogoutResult = try await GatewayConnection.shared.requestDecoded(
                method: .channelsLogout,
                params: params,
                timeoutMs: 15000)
            self.whatsappLoginMessage = result.cleared
                ? "Logged out and cleared credentials."
                : "No WhatsApp session found."
            self.whatsappLoginQrDataUrl = nil
        } catch {
            self.whatsappLoginMessage = error.localizedDescription
        }
        await self.refresh(probe: true)
    }

    func logoutTelegram() async {
        guard !self.telegramBusy else { return }
        self.telegramBusy = true
        defer { self.telegramBusy = false }
        do {
            let params: [String: AnyCodable] = [
                "channel": AnyCodable("telegram"),
            ]
            let result: ChannelLogoutResult = try await GatewayConnection.shared.requestDecoded(
                method: .channelsLogout,
                params: params,
                timeoutMs: 15000)
            if result.envToken == true {
                self.configStatus = "Telegram token still set via env; config cleared."
            } else {
                self.configStatus = result.cleared
                    ? "Telegram token cleared."
                    : "No Telegram token configured."
            }
            await self.loadConfig()
        } catch {
            self.configStatus = error.localizedDescription
        }
        await self.refresh(probe: true)
    }
}

private struct WhatsAppLoginStartResult: Codable {
    let qrDataUrl: String?
    let message: String
    let connected: Bool?
}

struct WhatsAppLoginWaitResult: Codable {
    let connected: Bool
    let message: String
    let qrDataUrl: String?
}

private struct ChannelLogoutResult: Codable {
    let channel: String?
    let accountId: String?
    let cleared: Bool
    let envToken: Bool?
}
