import AppKit
import Foundation
import OSLog

@MainActor
final class VoiceSessionCoordinator: ObservableObject {
    static let shared = VoiceSessionCoordinator()

    enum Source: String { case wakeWord, pushToTalk }

    struct Session {
        let token: UUID
        let source: Source
        var text: String
        var attributed: NSAttributedString?
        var isFinal: Bool
        var forwardConfig: VoiceWakeForwardConfig?
        var sendChime: VoiceWakeChime
        var autoSendDelay: TimeInterval?
    }

    private let logger = Logger(subsystem: "com.steipete.clawdis", category: "voicewake.coordinator")
    private var session: Session?

    // MARK: - API

    func startSession(
        source: Source,
        text: String,
        attributed: NSAttributedString? = nil,
        forwardEnabled: Bool = false) -> UUID
    {
        // If a send is in-flight, ignore new sessions to avoid token churn.
        if VoiceWakeOverlayController.shared.model.isSending {
            self.logger.info("coordinator drop start while sending")
            return self.session?.token ?? UUID()
        }

        let token = UUID()
        self.logger.info("coordinator start token=\(token.uuidString) source=\(source.rawValue) len=\(text.count)")
        let attributedText = attributed ?? VoiceWakeOverlayController.shared.makeAttributed(from: text)
        let session = Session(
            token: token,
            source: source,
            text: text,
            attributed: attributedText,
            isFinal: false,
            forwardConfig: forwardEnabled ? AppStateStore.shared.voiceWakeForwardConfig : nil,
            sendChime: .none,
            autoSendDelay: nil)
        self.session = session
        VoiceWakeOverlayController.shared.startSession(
            token: token,
            source: VoiceWakeOverlayController.Source(rawValue: source.rawValue) ?? .wakeWord,
            transcript: text,
            attributed: attributedText,
            forwardEnabled: forwardEnabled,
            isFinal: false)
        return token
    }

    func updatePartial(token: UUID, text: String, attributed: NSAttributedString? = nil) {
        guard let session, session.token == token else { return }
        self.session?.text = text
        self.session?.attributed = attributed
        VoiceWakeOverlayController.shared.updatePartial(token: token, transcript: text, attributed: attributed)
    }

    func finalize(
        token: UUID,
        text: String,
        forwardConfig: VoiceWakeForwardConfig,
        sendChime: VoiceWakeChime,
        autoSendAfter: TimeInterval?)
    {
        guard let session, session.token == token else { return }
        self.logger
            .info(
                "coordinator finalize token=\(token.uuidString) len=\(text.count) autoSendAfter=\(autoSendAfter ?? -1)")
        self.session?.text = text
        self.session?.isFinal = true
        self.session?.forwardConfig = forwardConfig
        self.session?.sendChime = sendChime
        self.session?.autoSendDelay = autoSendAfter

        let attributed = VoiceWakeOverlayController.shared.makeAttributed(from: text)
        VoiceWakeOverlayController.shared.presentFinal(
            token: token,
            transcript: text,
            forwardConfig: forwardConfig,
            autoSendAfter: autoSendAfter,
            sendChime: sendChime,
            attributed: attributed)
    }

    func sendNow(token: UUID, reason: String = "explicit") {
        guard let session, session.token == token else { return }
        let text = session.text.trimmingCharacters(in: .whitespacesAndNewlines)
        guard let forward = session.forwardConfig, forward.enabled else {
            self.logger.info("coordinator sendNow \(reason) no forward config -> dismiss")
            VoiceWakeOverlayController.shared.dismiss(token: token, reason: .explicit, outcome: .empty)
            self.clearSession()
            return
        }
        guard !text.isEmpty else {
            self.logger.info("coordinator sendNow \(reason) empty -> dismiss")
            VoiceWakeOverlayController.shared.dismiss(token: token, reason: .empty, outcome: .empty)
            self.clearSession()
            return
        }
        VoiceWakeOverlayController.shared.beginSendUI(token: token, sendChime: session.sendChime)
        Task.detached {
            _ = await VoiceWakeForwarder.forward(
                transcript: VoiceWakeForwarder.prefixedTranscript(text),
                config: forward)
        }
    }

    func dismiss(
        token: UUID,
        reason: VoiceWakeOverlayController.DismissReason,
        outcome: VoiceWakeOverlayController.SendOutcome)
    {
        guard let session, session.token == token else { return }
        VoiceWakeOverlayController.shared.dismiss(token: token, reason: reason, outcome: outcome)
        self.clearSession()
    }

    func updateLevel(token: UUID, _ level: Double) {
        guard let session, session.token == token else { return }
        VoiceWakeOverlayController.shared.updateLevel(token: token, level)
    }

    func snapshot() -> (token: UUID?, text: String, visible: Bool) {
        (self.session?.token, self.session?.text ?? "", VoiceWakeOverlayController.shared.isVisible)
    }

    // MARK: - Private

    private func clearSession() {
        self.session = nil
    }
}
