import AppKit
import SwiftUI

/// The settings window. Everything saves as you change it — there's no OK button
/// on purpose, so there's nothing to forget to press.
struct SettingsView: View {
    @ObservedObject var store: SettingsStore

    var body: some View {
        VStack(spacing: 0) {
            header

            ScrollView {
                VStack(alignment: .leading, spacing: 22) {
                    meetingsSection
                    Divider()
                    alienSection
                    Divider()
                    startupSection
                }
                .padding(20)
            }
            // Without this SwiftUI can open the window already scrolled past the
            // first section.
            .defaultScrollAnchor(.top)

            footer
        }
        .frame(width: 540, height: 640)
    }

    // MARK: - Header / footer

    private var header: some View {
        HStack(spacing: 10) {
            Text("👽").font(.system(size: 26))
            VStack(alignment: .leading, spacing: 1) {
                Text("Alien Reminder").font(.headline)
                Text("by Incuv8or · changes save automatically")
                    .font(.caption).foregroundStyle(.secondary)
            }
            Spacer()
        }
        .padding(.horizontal, 20)
        .padding(.vertical, 14)
        .background(.quaternary.opacity(0.35))
    }

    private var footer: some View {
        HStack {
            Button("Test Fire") { store.onTestFire?() }
                .keyboardShortcut(.defaultAction)
            Text("or press ⌃⌥⌘A anywhere")
                .font(.caption).foregroundStyle(.secondary)
            Spacer()
        }
        .padding(.horizontal, 20)
        .padding(.vertical, 12)
        .background(.quaternary.opacity(0.35))
    }

    // MARK: - Meetings

    private var meetingsSection: some View {
        VStack(alignment: .leading, spacing: 12) {
            sectionTitle("Meetings", subtitle: "From the Calendar app on this Mac")

            Toggle("Warn me before meetings", isOn: binding(\.calendarEnabled))
                .toggleStyle(.switch)

            if store.config.calendarEnabled {
                Stepper(value: binding(\.meetingLeadMinutes), in: 1...60) {
                    Text("Warn me **\(store.config.meetingLeadMinutes)** minutes before")
                }
                .frame(maxWidth: 300)

                if !store.calendarAccess {
                    calendarAccessPrompt
                } else if store.calendars.isEmpty {
                    Text("No calendars found on this Mac.")
                        .font(.callout).foregroundStyle(.secondary)
                } else {
                    calendarChecklist
                }
            }
        }
    }

    private var calendarAccessPrompt: some View {
        VStack(alignment: .leading, spacing: 8) {
            Text("Calendar access hasn't been granted yet.")
                .font(.callout)
            Text("Everything this app knows comes from the Calendar app, so it can't do "
                 + "anything until you allow it.")
                .font(.caption).foregroundStyle(.secondary)
                .fixedSize(horizontal: false, vertical: true)
            HStack(spacing: 10) {
                Button("Grant Access…") { store.onRequestCalendarAccess?() }
                Button("Open Privacy Settings") {
                    let url = URL(string:
                        "x-apple.systempreferences:com.apple.preference.security?Privacy_Calendars")!
                    NSWorkspace.shared.open(url)
                }
            }
        }
        .padding(12)
        .background(.quaternary.opacity(0.3), in: RoundedRectangle(cornerRadius: 8))
    }

    private var calendarChecklist: some View {
        VStack(alignment: .leading, spacing: 8) {
            HStack {
                Text("Which calendars?").font(.subheadline.weight(.semibold))
                Spacer()
                Button("All") { store.setAllCalendars(on: true) }
                    .buttonStyle(.link)
                Button("None") { store.setAllCalendars(on: false) }
                    .buttonStyle(.link)
            }

            Text("Grouped by the account they come from. Untick a client's calendar to "
                 + "stop their meetings showing up.")
                .font(.caption).foregroundStyle(.secondary)
                .fixedSize(horizontal: false, vertical: true)

            VStack(alignment: .leading, spacing: 14) {
                ForEach(store.calendarsByAccount, id: \.account) { group in
                    VStack(alignment: .leading, spacing: 5) {
                        Text(group.account.uppercased())
                            .font(.caption2.weight(.bold))
                            .foregroundStyle(.secondary)
                        ForEach(group.items) { cal in
                            Toggle(isOn: Binding(
                                get: { store.isCalendarOn(cal.id) },
                                set: { store.setCalendar(cal.id, on: $0) })
                            ) {
                                HStack(spacing: 7) {
                                    Circle()
                                        .fill(Color(hex: cal.colorHex))
                                        .frame(width: 9, height: 9)
                                    Text(cal.title)
                                }
                            }
                        }
                    }
                }
            }
            .padding(12)
            .background(.quaternary.opacity(0.25), in: RoundedRectangle(cornerRadius: 8))
        }
    }

    // MARK: - The alien itself

    private var alienSection: some View {
        VStack(alignment: .leading, spacing: 12) {
            sectionTitle("The alien", subtitle: "How it looks and behaves")

            VStack(alignment: .leading, spacing: 6) {
                Picker("Colours", selection: binding(\.fixedPalette)) {
                    Text("Rotate through all three").tag("")
                    ForEach(Palette.all, id: \.id) { p in
                        Text(p.name).tag(p.id)
                    }
                }
                .frame(maxWidth: 340)

                // Just the colours. No captions — the swatches speak for themselves.
                HStack(alignment: .center, spacing: 18) {
                    ForEach(Palette.all, id: \.id) { p in
                        HStack(spacing: 3) {
                            ForEach([p.skin.0, p.skin.1, p.hull.0, p.hull.1, p.glow],
                                    id: \.self) { hex in
                                RoundedRectangle(cornerRadius: 2)
                                    .fill(Color(hex: hex))
                                    .frame(width: 15, height: 15)
                            }
                        }
                    }
                }
            }

            VStack(alignment: .leading, spacing: 4) {
                Text("Size — **\(String(format: "%.1f", store.config.sceneScale))×**")
                Slider(value: binding(\.sceneScale), in: 0.6...2.5, step: 0.1) {
                    EmptyView()
                } minimumValueLabel: {
                    Text("small").font(.caption2)
                } maximumValueLabel: {
                    Text("huge").font(.caption2)
                }
                Text("1.6× or more films well if you're cropping to 9:16.")
                    .font(.caption).foregroundStyle(.secondary)
            }

            Stepper(value: binding(\.pollSeconds), in: 15...600, step: 15) {
                Text("Check the calendar every **\(store.config.pollSeconds)s**")
            }
            .frame(maxWidth: 340)

            Text("Click the ✕ on the bubble to dismiss a reminder early, "
                 + "or press ⌃⌥⌘X.")
                .font(.caption).foregroundStyle(.secondary)
        }
    }

    // MARK: - Startup

    private var startupSection: some View {
        VStack(alignment: .leading, spacing: 8) {
            sectionTitle("Startup", subtitle: "So it's always watching")

            Toggle("Open at login", isOn: binding(\.openAtLogin))
                .toggleStyle(.switch)

            if let error = store.loginItemError {
                Label(error, systemImage: "exclamationmark.triangle.fill")
                    .font(.caption)
                    .foregroundStyle(.orange)
                    .fixedSize(horizontal: false, vertical: true)
            } else {
                Text("Starts the alien quietly in the menu bar when you log in.")
                    .font(.caption).foregroundStyle(.secondary)
            }
        }
    }

    // MARK: - Helpers

    private func sectionTitle(_ title: String, subtitle: String) -> some View {
        VStack(alignment: .leading, spacing: 1) {
            Text(title).font(.title3.weight(.semibold))
            Text(subtitle).font(.caption).foregroundStyle(.secondary)
        }
    }

    /// Writes through to `store.config`, which saves on every mutation.
    private func binding<T>(_ keyPath: WritableKeyPath<Config, T>) -> Binding<T> {
        Binding(
            get: { store.config[keyPath: keyPath] },
            set: { store.config[keyPath: keyPath] = $0 })
    }
}

extension Color {
    /// "#RRGGBB" → Color. Falls back to the brand violet.
    init(hex: String) {
        let cleaned = hex.hasPrefix("#") ? String(hex.dropFirst()) : hex
        guard cleaned.count == 6, let value = UInt32(cleaned, radix: 16) else {
            self = Color(red: 0.52, green: 0.22, blue: 0.96)
            return
        }
        self = Color(
            red: Double((value >> 16) & 0xFF) / 255,
            green: Double((value >> 8) & 0xFF) / 255,
            blue: Double(value & 0xFF) / 255)
    }
}
