import SwiftUI
import SwiftData
import AVFoundation

// MARK: - HomeView
/// The main launch screen of CelestiaTrace.
/// Features a minimalist, line-drawn aesthetic with a central record button
/// and a subtle technical grid background.
struct HomeView: View {
    @Environment(\.modelContext) private var modelContext
    @ObservedObject private var bleManager: BLEManager = .shared
    @ObservedObject private var authManager: AuthManager = .shared
    @Query(sort: \CelestiaSession.startTime, order: .reverse)
    private var sessions: [CelestiaSession]

    @State private var activeRecording: ActiveRecordingPresentation?
    @State private var completedRecordingSession: CelestiaSession?
    @State private var detector = RecordingEnvironmentDetector()
    @State private var dismissedWarnings: Set<String> = []
    @State private var showRecordingSourcePicker = false
    @State private var pendingRecordingSource: RecordingSourceChoice?

    var onRecordingFinished: ((CelestiaSession) -> Void)? = nil

    var body: some View {
        NavigationStack {
            ZStack {
                // Dynamic clean background
                Color.spaceBlack
                    .ignoresSafeArea()

                // Subtle technical grid background
                businessGrid

                // Main content
                VStack(spacing: 0) {
                    topBar
                        .padding(.top, 16)

                    if let firstWarning = detector.activeWarnings.first(where: { !dismissedWarnings.contains($0) }) {
                        DiscreetWarningBanner(warning: firstWarning) {
                            withAnimation {
                                _ = dismissedWarnings.insert(firstWarning)
                            }
                        }
                        .padding(.top, 12)
                    }

                    Spacer()

                    recordSection

                    Spacer()
                }
                .padding(.horizontal, 24)
            }
            .navigationBarHidden(true)
            .onAppear {
                detector.checkEnvironment()
                restoreActiveRecordingIfNeeded()
            }
            .onOpenURL(perform: handleRecordingURL)
            .fullScreenCover(item: $activeRecording, onDismiss: {
                if let session = completedRecordingSession {
                    completedRecordingSession = nil
                    onRecordingFinished?(session)
                }
            }) { recording in
                ActiveRecordingView(
                    session: recording.session,
                    initialDuration: recording.initialDuration,
                    recordingSource: recording.source,
                    initialAction: recording.initialAction
                ) {
                    completedRecordingSession = recording.session
                }
            }
            .sheet(isPresented: $showRecordingSourcePicker, onDismiss: {
                guard let source = pendingRecordingSource else { return }
                pendingRecordingSource = nil
                createSessionAndNavigate(source: source)
            }) {
                RecordingSourcePickerView(devices: connectedSparkDevices) { source in
                    pendingRecordingSource = source
                    showRecordingSourcePicker = false
                }
            }
        }
    }

    // MARK: - Top Bar

    private var topBar: some View {
        HStack(alignment: .top) {
            VStack(alignment: .leading, spacing: 4) {
                Text("星痕现场记录")
                    .font(.system(size: 11, weight: .bold, design: .monospaced))
                    .foregroundStyle(Color.primary.opacity(0.8))
                    .tracking(3)

                Text(currentDateFormatted)
                    .font(.system(size: 13, weight: .regular))
                    .foregroundStyle(Color.secondary)
            }
            Spacer()
        }
    }

    // MARK: - Record Section

    private var recordSection: some View {
        VStack(spacing: 24) {
            PulsingRecordButton {
                beginNewRecording()
            }

            Button {
                beginNewRecording()
            } label: {
                Text("开始")
                    .font(.system(size: 16, weight: .medium))
                    .foregroundStyle(Color.primary.opacity(0.88))
                    .tracking(0.4)
            }
            .buttonStyle(.plain)
        }
    }

    // MARK: - Technical Grid Background

    private var businessGrid: some View {
        Canvas { context, size in
            let step: CGFloat = 40
            let cols = Int(size.width / step)
            let rows = Int(size.height / step)
            
            for col in 0...cols {
                let x = CGFloat(col) * step
                var path = Path()
                path.move(to: CGPoint(x: x, y: 0))
                path.addLine(to: CGPoint(x: x, y: size.height))
                context.stroke(path, with: .color(Color.primary.opacity(0.015)), lineWidth: 0.5)
            }
            for row in 0...rows {
                let y = CGFloat(row) * step
                var path = Path()
                path.move(to: CGPoint(x: 0, y: y))
                path.addLine(to: CGPoint(x: size.width, y: y))
                context.stroke(path, with: .color(Color.primary.opacity(0.015)), lineWidth: 0.5)
            }
        }
        .ignoresSafeArea()
        .allowsHitTesting(false)
    }

    // MARK: - Actions

    private func beginNewRecording() {
        if connectedSparkDevices.isEmpty {
            createSessionAndNavigate(source: .iPhone)
        } else {
            showRecordingSourcePicker = true
        }
    }

    private func createSessionAndNavigate(source: RecordingSourceChoice) {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyyMMdd_HHmm"
        let title = "\(formatter.string(from: Date()))_现场记录"

        let session = CelestiaSession(title: title)
        modelContext.insert(session)
        try? modelContext.save()
        RecordingRecoveryStore.begin(sessionID: session.id, source: source)

        HapticManager.trigger(.recordStart)
        activeRecording = ActiveRecordingPresentation(
            session: session,
            source: source,
            initialDuration: 0,
            initialAction: nil
        )
    }

    private func restoreActiveRecordingIfNeeded(
        sessionID requestedSessionID: UUID? = nil,
        action: RecordingLiveActivityAction? = nil
    ) {
        guard activeRecording == nil else { return }

        let persistedID = RecordingRecoveryStore.activeSessionID
        let activityID = RecordingLiveActivityManager.shared.recoverableSessionID
        let session: CelestiaSession?
        if let requestedSessionID {
            // An explicit Live Activity action must never fall through to a
            // different unfinished recording (especially for the stop action).
            session = sessions.first {
                $0.id == requestedSessionID && $0.endTime == nil
            }
        } else {
            session = [persistedID, activityID]
                .compactMap { $0 }
                .compactMap { candidateID in
                    sessions.first {
                        $0.id == candidateID && $0.endTime == nil
                    }
                }
                .first
                ?? sessions.first {
                    $0.endTime == nil
                        && Date().timeIntervalSince($0.startTime) < 24 * 60 * 60
                }
        }

        guard let session else {
            if requestedSessionID == nil, persistedID != nil {
                RecordingRecoveryStore.clear()
            }
            return
        }

        let source = RecordingRecoveryStore.source(for: session.id) ?? .iPhone
        RecordingRecoveryStore.begin(sessionID: session.id, source: source, preservingSegment: true)

        Task { @MainActor in
            await recoverPendingSegment(for: session)
            let duration = await recordedDuration(for: session.localAudioPath)
            guard activeRecording == nil, session.endTime == nil else { return }
            activeRecording = ActiveRecordingPresentation(
                session: session,
                source: source,
                initialDuration: duration,
                initialAction: action
            )
        }
    }

    private func recoverPendingSegment(for session: CelestiaSession) async {
        guard let pendingPath = RecordingRecoveryStore.pendingSegmentPath(for: session.id),
              let pendingURL = AudioPathHelper.resolveURL(for: pendingPath) else {
            return
        }

        let existingURL = AudioPathHelper.resolveURL(for: session.localAudioPath)
        if existingURL?.standardizedFileURL == pendingURL.standardizedFileURL {
            RecordingRecoveryStore.clearPendingSegment(for: session.id)
            return
        }

        let recoveredURL = await AudioMerger.mergeAudioFiles(
            firstURL: existingURL,
            secondURL: pendingURL
        )
        session.localAudioPath = AudioPathHelper.relativePath(from: recoveredURL.path)
        session.markContentModified()
        try? modelContext.save()
        RecordingRecoveryStore.clearPendingSegment(for: session.id)
    }

    private func recordedDuration(for path: String?) async -> TimeInterval {
        guard let url = AudioPathHelper.resolveURL(for: path) else { return 0 }
        let asset = AVURLAsset(url: url)
        guard let duration = try? await asset.load(.duration) else { return 0 }
        let seconds = duration.seconds
        return seconds.isFinite && seconds > 0 ? seconds : 0
    }

    private func handleRecordingURL(_ url: URL) {
        guard let route = RecordingLiveActivityRoute(url: url) else { return }
        restoreActiveRecordingIfNeeded(
            sessionID: route.sessionID,
            action: route.action
        )
    }

    private var connectedSparkDevices: [BoundDevice] {
        guard let userID = authManager.currentUser?.id else { return [] }
        return bleManager.connectedDevices(forUserId: userID)
            .sorted { $0.boundAt < $1.boundAt }
    }

    // MARK: - Helpers

    private var currentDateFormatted: String {
        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "zh_Hans")
        formatter.dateFormat = "yyyy年M月d日 EEEE"
        return formatter.string(from: Date())
    }
}

private struct ActiveRecordingPresentation: Identifiable {
    let id = UUID()
    let session: CelestiaSession
    let source: RecordingSourceChoice
    let initialDuration: TimeInterval
    let initialAction: RecordingLiveActivityAction?
}

enum RecordingLiveActivityAction: String {
    case photo
    case note
    case togglePause = "toggle-pause"
    case stop
}

struct RecordingLiveActivityRoute {
    let sessionID: UUID
    let action: RecordingLiveActivityAction?

    init?(url: URL) {
        guard url.scheme == "celestiatrace", url.host == "recording" else { return nil }
        let components = url.pathComponents.filter { $0 != "/" }
        guard let first = components.first, let sessionID = UUID(uuidString: first) else {
            return nil
        }
        self.sessionID = sessionID
        self.action = components.dropFirst().first.flatMap(RecordingLiveActivityAction.init(rawValue:))
    }
}

enum RecordingRecoveryStore {
    private static let defaults = UserDefaults.standard
    private static let sessionIDKey = "activeRecording.sessionID"
    private static let sourceKindKey = "activeRecording.sourceKind"
    private static let sourceDeviceIDKey = "activeRecording.sourceDeviceID"
    private static let sourceNameKey = "activeRecording.sourceName"
    private static let pendingSegmentPathKey = "activeRecording.pendingSegmentPath"

    static var activeSessionID: UUID? {
        defaults.string(forKey: sessionIDKey).flatMap(UUID.init(uuidString:))
    }

    static func begin(
        sessionID: UUID,
        source: RecordingSourceChoice,
        preservingSegment: Bool = false
    ) {
        if !preservingSegment || activeSessionID != sessionID {
            defaults.removeObject(forKey: pendingSegmentPathKey)
        }
        defaults.set(sessionID.uuidString, forKey: sessionIDKey)
        switch source {
        case .iPhone:
            defaults.set("iphone", forKey: sourceKindKey)
            defaults.removeObject(forKey: sourceDeviceIDKey)
            defaults.removeObject(forKey: sourceNameKey)
        case .spark(let deviceID, let displayName):
            defaults.set("spark", forKey: sourceKindKey)
            defaults.set(deviceID, forKey: sourceDeviceIDKey)
            defaults.set(displayName, forKey: sourceNameKey)
        }
    }

    static func source(for sessionID: UUID) -> RecordingSourceChoice? {
        guard activeSessionID == sessionID else { return nil }
        switch defaults.string(forKey: sourceKindKey) {
        case "iphone":
            return .iPhone
        case "spark":
            guard let deviceID = defaults.string(forKey: sourceDeviceIDKey),
                  let name = defaults.string(forKey: sourceNameKey) else { return nil }
            return .spark(deviceID: deviceID, displayName: name)
        default:
            return nil
        }
    }

    static func setPendingSegment(_ url: URL, for sessionID: UUID) {
        guard activeSessionID == sessionID else { return }
        defaults.set(AudioPathHelper.relativePath(from: url.path), forKey: pendingSegmentPathKey)
    }

    static func pendingSegmentPath(for sessionID: UUID) -> String? {
        guard activeSessionID == sessionID else { return nil }
        return defaults.string(forKey: pendingSegmentPathKey)
    }

    static func clearPendingSegment(for sessionID: UUID) {
        guard activeSessionID == sessionID else { return }
        defaults.removeObject(forKey: pendingSegmentPathKey)
    }

    static func clear(sessionID: UUID? = nil) {
        if let sessionID, activeSessionID != sessionID { return }
        [
            sessionIDKey,
            sourceKindKey,
            sourceDeviceIDKey,
            sourceNameKey,
            pendingSegmentPathKey
        ].forEach(defaults.removeObject(forKey:))
    }
}

// MARK: - Recording source picker

/// Shared by new recordings and continuation recordings. When at least one
/// connected Spark exists, the first Spark is selected by default.
struct RecordingSourcePickerView: View {
    @Environment(\.dismiss) private var dismiss

    let devices: [BoundDevice]
    let onConfirm: (RecordingSourceChoice) -> Void
    @State private var selection: RecordingSourceChoice

    init(devices: [BoundDevice], onConfirm: @escaping (RecordingSourceChoice) -> Void) {
        self.devices = devices
        self.onConfirm = onConfirm
        if let first = devices.first {
            _selection = State(initialValue: .spark(deviceID: first.id, displayName: first.name))
        } else {
            _selection = State(initialValue: .iPhone)
        }
    }

    var body: some View {
        NavigationStack {
            ZStack {
                Color.spaceBlack.ignoresSafeArea()
                VStack(alignment: .leading, spacing: 14) {
                    Text("选择本次现场记录使用的录音设备")
                        .font(.system(size: 13))
                        .foregroundStyle(Color.secondary)

                    ForEach(Array(devices.enumerated()), id: \.element.id) { index, device in
                        sourceRow(
                            source: .spark(deviceID: device.id, displayName: device.name),
                            title: device.name,
                            subtitle: sparkSubtitle(device),
                            badge: index == 0 ? "默认" : nil
                        )
                    }

                    sourceRow(
                        source: .iPhone,
                        title: "iPhone 麦克风",
                        subtitle: "使用手机内置或当前系统音频输入",
                        badge: devices.isEmpty ? "默认" : nil
                    )

                    Button {
                        onConfirm(selection)
                    } label: {
                        Text("开始现场记录")
                            .font(.system(size: 14, weight: .semibold))
                            .foregroundStyle(Color.spaceBlack)
                            .frame(maxWidth: .infinity)
                            .padding(.vertical, 12)
                            .background(Color.primary)
                            .cornerRadius(8)
                    }
                    .padding(.top, 8)

                    Spacer()
                }
                .padding(20)
            }
            .navigationTitle("录音设备")
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button("取消") { dismiss() }
                        .foregroundStyle(Color.secondary)
                }
            }
        }
        .presentationDetents([.medium, .large])
        .presentationBackground(Color.spaceBlack)
    }

    private func sourceRow(
        source: RecordingSourceChoice,
        title: String,
        subtitle: String,
        badge: String?
    ) -> some View {
        Button {
            selection = source
        } label: {
            HStack(spacing: 12) {
                Image(systemName: source.systemImage)
                    .font(.system(size: 19, weight: .light))
                    .foregroundStyle(Color.primary)
                    .frame(width: 34)

                VStack(alignment: .leading, spacing: 4) {
                    HStack(spacing: 7) {
                        Text(title)
                            .font(.system(size: 14, weight: .semibold))
                            .foregroundStyle(Color.primary)
                        if let badge {
                            Text(badge)
                                .font(.system(size: 9, weight: .bold))
                                .foregroundStyle(Color.spaceBlack)
                                .padding(.horizontal, 6)
                                .padding(.vertical, 2)
                                .background(Color.primary)
                                .cornerRadius(4)
                        }
                    }
                    Text(subtitle)
                        .font(.system(size: 11))
                        .foregroundStyle(Color.secondary)
                }

                Spacer()

                Image(systemName: selection == source ? "checkmark.circle.fill" : "circle")
                    .font(.system(size: 18))
                    .foregroundStyle(selection == source ? Color.primary : Color.secondary)
            }
            .padding(14)
            .background(Color.cardBackground.opacity(selection == source ? 0.55 : 0.25))
            .businessBorder(cornerRadius: 10)
        }
        .buttonStyle(.plain)
    }

    private func sparkSubtitle(_ device: BoundDevice) -> String {
        var parts = ["已连接"]
        if let battery = device.batteryLevel { parts.append("电量 \(battery)%") }
        if let free = device.freeStorageMB { parts.append("剩余 \(free) MB") }
        return parts.joined(separator: " · ")
    }
}

// MARK: - Preview

#Preview {
    HomeView()
        .modelContainer(for: CelestiaSession.self, inMemory: true)
}
