import SwiftUI
import Combine

struct ClockView: View {
    @State private var currentTime = Date()
    private let secondTimer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    private let minuteTimer = Timer.publish(every: 60, on: .main, in: .common).autoconnect()
    
    // Persist the style as JSON in AppStorage
    @AppStorage(ClockStyle.appStorageKey) private var styleJSON: Data = {
        let def = ClockStyle()
        return (try? JSONEncoder().encode(def)) ?? Data()
    }()
    
    @State private var style: ClockStyle = ClockStyle()
    @State private var showSettings = false
    
    // Display mode (full-screen clock)
    @State private var isDisplayMode = false
    
    var body: some View {
        ZStack {
            style.backgroundColor
                .ignoresSafeArea()
            
            // Animate the whole visible content to soften layout changes
            VStack(spacing: 12) {
                // Battery/date overlay — always shown if enabled
                if style.showBattery || style.showDate {
                    TopOverlay(
                        showBattery: style.showBattery,
                        showDate: style.showDate,
                        color: style.digitColor.opacity(0.9),
                        overallOpacity: style.overlayOpacity
                    )
                    .padding(.top, 8)
                    .padding(.horizontal, 16)
                    .transition(.opacity)
                }
                
                Spacer()
                
                // Clock
                SegmentedTimeView(
                    date: currentTime,
                    use24Hour: style.use24Hour,
                    showSeconds: style.showSeconds,
                    digitColor: style.digitColor,
                    glowIntensity: style.glowIntensity,
                    manualScale: style.digitScale,  // 0.0 ... 1.0 as percentage of stretched
                    stretched: style.stretched,
                    showAmPmBadge: style.showAmPmBadge,
                    clockOpacity: style.clockOpacity
                )
                .padding(.horizontal, 12)
                .transition(.opacity)
                
                Spacer()
            }
            // Subtle scale on the entire content during the transition
            .scaleEffect(isDisplayMode ? 1.0 : 0.995)
            .opacity(isDisplayMode ? 1.0 : 1.0) // keep opacity, but transition hooks are kept above
            .animation(.easeInOut(duration: 0.28), value: isDisplayMode)
        }
        .navigationTitle(isDisplayMode ? "" : "Clock")
        .toolbar {
            if !isDisplayMode {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button {
                        showSettings = true
                    } label: {
                        Image(systemName: "gear")
                            .font(.title2)
                            .transition(.opacity)
                    }
                }
            }
        }
        // Hide the navigation bar entirely in display mode
        .navigationBarBackButtonHidden(isDisplayMode)
        .toolbar(isDisplayMode ? .hidden : .automatic)
        .onAppear {
            loadStyle()
            // Ensure correct tab bar visibility if we reappear while in display mode
            setTabBarHidden(isDisplayMode, animated: false)
        }
        .onDisappear {
            // Restore tab bar when leaving this screen
            setTabBarHidden(false, animated: false)
        }
        .onReceive(secondTimer) { now in
            currentTime = now
        }
        .onReceive(minuteTimer) { _ in
            guard style.randomizeColor else { return }
            style.digitColorHex = Self.randomBrightColorHex()
            saveStyle()
        }
        .sheet(isPresented: $showSettings) {
            ClockSettingsView(style: $style, onCommit: saveStyle)
                .presentationDetents([.medium, .large])
        }
        .onChange(of: style) { _ in
            saveStyle()
        }
        // Long-press anywhere to toggle display mode
        .contentShape(Rectangle())
        .simultaneousGesture(
            LongPressGesture(minimumDuration: 0.6)
                .onEnded { _ in
                    withAnimation(.easeInOut(duration: 0.28)) {
                        isDisplayMode.toggle()
                        setTabBarHidden(isDisplayMode, animated: true)
                    }
                }
        )
    }
    
    private func loadStyle() {
        if let decoded = try? JSONDecoder().decode(ClockStyle.self, from: styleJSON) {
            style = decoded
        } else {
            style = ClockStyle()
            saveStyle()
        }
    }
    
    private func saveStyle() {
        if let data = try? JSONEncoder().encode(style) {
            styleJSON = data
        }
    }
    
    private static func randomBrightColorHex() -> String {
        let hue = Double.random(in: 0...1)
        let color = Color(hue: hue, saturation: 0.9, brightness: 0.95)
        return color.toHex() ?? "#FFFFFF"
    }
    
    // MARK: - Tab bar visibility helper (UIKit)
    private func setTabBarHidden(_ hidden: Bool, animated: Bool) {
        #if canImport(UIKit)
        guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
              let window = windowScene.windows.first,
              let tabBarController = window.rootViewController?.findTabBarController() else { return }
        
        let tabBar = tabBarController.tabBar
        let changes = {
            tabBar.alpha = hidden ? 0 : 1
        }
        if animated {
            UIView.animate(withDuration: 0.25, animations: changes)
        } else {
            changes()
        }
        tabBar.isUserInteractionEnabled = !hidden
        #endif
    }
}

#if canImport(UIKit)
private extension UIViewController {
    func findTabBarController() -> UITabBarController? {
        if let tbc = self as? UITabBarController { return tbc }
        for child in children {
            if let tbc = child.findTabBarController() { return tbc }
        }
        if let presented = presentedViewController {
            return presented.findTabBarController()
        }
        if let nav = self as? UINavigationController {
            return nav.visibleViewController?.findTabBarController()
        }
        return parent?.findTabBarController()
    }
}
#endif

// MARK: - Segmented Time View

private struct SegmentedTimeView: View {
    let date: Date
    let use24Hour: Bool
    let showSeconds: Bool
    let digitColor: Color
    let glowIntensity: Double
    let manualScale: Double   // 0.0 ... 1.0 percent of fitted scale
    let stretched: Bool
    let showAmPmBadge: Bool
    let clockOpacity: Double  // 0.0 ... 1.0 overall opacity
    
    // Formatters
    private static let hour24DF: DateFormatter = {
        let df = DateFormatter()
        df.locale = Locale(identifier: "en_US_POSIX")
        df.dateFormat = "HH"
        return df
    }()
    private static let hour12DF: DateFormatter = {
        let df = DateFormatter()
        df.locale = Locale(identifier: "en_US_POSIX")
        df.dateFormat = "h"
        return df
    }()
    private static let minuteDF: DateFormatter = {
        let df = DateFormatter()
        df.locale = Locale(identifier: "en_US_POSIX")
        df.dateFormat = "mm"
        return df
    }()
    private static let secondDF: DateFormatter = {
        let df = DateFormatter()
        df.locale = Locale(identifier: "en_US_POSIX")
        df.dateFormat = "ss"
        return df
    }()
    private static let ampmDF: DateFormatter = {
        let df = DateFormatter()
        df.locale = Locale(identifier: "en_US_POSIX")
        df.dateFormat = "a"
        return df
    }()
    
    var body: some View {
        GeometryReader { proxy in
            let size = proxy.size
            let portrait = size.height >= size.width
            let baseFontSize = dynamicBaseFontSize(containerWidth: size.width, containerHeight: size.height)
            let ampmFontSize = baseFontSize * 0.20
            
            // Segments
            let hour = use24Hour ? Self.hour24DF.string(from: date) : Self.hour12DF.string(from: date)
            let minute = Self.minuteDF.string(from: date)
            let secondsText = Self.secondDF.string(from: date)
            let ampmText = Self.ampmDF.string(from: date)
            let showAMPM = !use24Hour && showAmPmBadge
            
            // Measure intrinsic sizes
            let digitUIFont = UIFont.systemFont(ofSize: baseFontSize, weight: .bold)
            let ampmUIFont = UIFont.systemFont(ofSize: ampmFontSize, weight: .bold)
            let hourSize = measure(text: hour, font: digitUIFont)
            let minuteSize = measure(text: minute, font: digitUIFont)
            let secondsSize = showSeconds ? measure(text: secondsText, font: digitUIFont) : .zero
            let ampmSize = showAMPM ? measure(text: ampmText, font: ampmUIFont) : .zero
            
            // Separators
            let dotDiameter = baseFontSize * 0.20
            let hSpacing = baseFontSize * 0.18
            let vSpacing = baseFontSize * 0.22
            let horizontalSepSize = CGSize(width: dotDiameter * 2 + hSpacing, height: dotDiameter)
            let verticalSepSize = CGSize(width: dotDiameter, height: dotDiameter * 2 + vSpacing)
            
            // Virtual layout size
            let (totalWidth, totalHeight): (CGFloat, CGFloat) = {
                if portrait {
                    var widths: [CGFloat] = [hourSize.width, minuteSize.width]
                    var totalH: CGFloat = hourSize.height + minuteSize.height
                    if showAMPM {
                        widths.append(ampmSize.width)
                        totalH += ampmSize.height
                    } else {
                        widths.append(horizontalSepSize.width)
                        totalH += horizontalSepSize.height
                    }
                    if showSeconds {
                        widths.append(contentsOf: [horizontalSepSize.width, secondsSize.width])
                        totalH += horizontalSepSize.height + secondsSize.height
                    }
                    return (widths.max() ?? 0, totalH)
                } else {
                    var totalW: CGFloat = hourSize.width + minuteSize.width
                    var heights: [CGFloat] = [hourSize.height, minuteSize.height]
                    if showAMPM {
                        totalW += ampmSize.width
                        heights.append(ampmSize.height)
                    } else {
                        totalW += verticalSepSize.width
                        heights.append(verticalSepSize.height)
                    }
                    if showSeconds {
                        totalW += verticalSepSize.width + secondsSize.width
                        heights.append(contentsOf: [verticalSepSize.height, secondsSize.height])
                    }
                    return (totalW, heights.max() ?? 0)
                }
            }()
            
            // Scale to fit
            let safeInsetW: CGFloat = 8
            let safeInsetH: CGFloat = 8
            let availableW = max(1, size.width - safeInsetW * 2)
            let availableH = max(1, size.height - safeInsetH * 2)
            let widthScale = availableW / max(totalWidth, 1)
            let heightScale = availableH / max(totalHeight, 1)
            let fittedScale = max(0.1, min(widthScale, heightScale))
            let manualPercent = max(0.0, min(manualScale, 1.0))
            let effectiveScale = stretched ? fittedScale : max(0.05, fittedScale * CGFloat(manualPercent))
            
            Group {
                if portrait {
                    VStack(spacing: 0) {
                        segment(hour, baseFontSize: baseFontSize, overallOpacity: clockOpacity)
                        if showAMPM {
                            segment(ampmText, baseFontSize: ampmFontSize, overallOpacity: clockOpacity)
                        } else {
                            horizontalColon(dotDiameter: dotDiameter, spacing: hSpacing, overallOpacity: clockOpacity)
                        }
                        segment(minute, baseFontSize: baseFontSize, overallOpacity: clockOpacity)
                        if showSeconds {
                            horizontalColon(dotDiameter: dotDiameter, spacing: hSpacing, overallOpacity: clockOpacity)
                            segment(secondsText, baseFontSize: baseFontSize, overallOpacity: clockOpacity)
                        }
                    }
                } else {
                    HStack(spacing: 0) {
                        segment(hour, baseFontSize: baseFontSize, overallOpacity: clockOpacity)
                        if showAMPM {
                            segment(ampmText, baseFontSize: ampmFontSize, overallOpacity: clockOpacity)
                        } else {
                            verticalColon(dotDiameter: dotDiameter, spacing: vSpacing, overallOpacity: clockOpacity)
                        }
                        segment(minute, baseFontSize: baseFontSize, overallOpacity: clockOpacity)
                        if showSeconds {
                            verticalColon(dotDiameter: dotDiameter, spacing: vSpacing, overallOpacity: clockOpacity)
                            segment(secondsText, baseFontSize: baseFontSize, overallOpacity: clockOpacity)
                        }
                    }
                }
            }
            .frame(width: size.width, height: size.height, alignment: .center)
            // Animate scale changes caused by geometry updates (e.g., hiding bars)
            .scaleEffect(effectiveScale, anchor: .center)
            .animation(.easeInOut(duration: 0.28), value: effectiveScale)
            .minimumScaleFactor(0.1)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
    
    private func segment(_ text: String, baseFontSize: CGFloat, overallOpacity: Double) -> some View {
        let clamped = max(0.0, min(overallOpacity, 1.0))
        return ZStack {
            Text(text)
                .font(.system(size: baseFontSize, weight: .bold, design: .rounded))
                .foregroundColor(digitColor)
                .blur(radius: glowRadius())
                .opacity(glowOpacity() * clamped)
            Text(text)
                .font(.system(size: baseFontSize, weight: .bold, design: .rounded))
                .foregroundColor(digitColor)
                .opacity(clamped)
        }
        .fixedSize(horizontal: true, vertical: true)
        .lineLimit(1)
        .allowsTightening(true)
        .multilineTextAlignment(.center)
    }
    
    private func horizontalColon(dotDiameter: CGFloat, spacing: CGFloat, overallOpacity: Double) -> some View {
        let clamped = max(0.0, min(overallOpacity, 1.0))
        return HStack(spacing: spacing) {
            dotCircle(size: dotDiameter, overallOpacity: clamped)
            dotCircle(size: dotDiameter, overallOpacity: clamped)
        }
        .fixedSize(horizontal: true, vertical: true)
        .accessibilityHidden(true)
    }
    
    private func verticalColon(dotDiameter: CGFloat, spacing: CGFloat, overallOpacity: Double) -> some View {
        let clamped = max(0.0, min(overallOpacity, 1.0))
        return VStack(spacing: spacing) {
            dotCircle(size: dotDiameter, overallOpacity: clamped)
            dotCircle(size: dotDiameter, overallOpacity: clamped)
        }
        .fixedSize(horizontal: true, vertical: true)
        .accessibilityHidden(true)
    }
    
    private func dotCircle(size: CGFloat, overallOpacity: Double) -> some View {
        ZStack {
            Circle()
                .fill(digitColor)
                .frame(width: size, height: size)
                .blur(radius: glowRadius())
                .opacity(glowOpacity() * overallOpacity)
            Circle()
                .fill(digitColor)
                .frame(width: size, height: size)
                .opacity(overallOpacity)
        }
    }
    
    private func dynamicBaseFontSize(containerWidth: CGFloat, containerHeight: CGFloat) -> CGFloat {
        let shortest = min(containerWidth, containerHeight)
        return min(shortest * 0.28, 220)
    }
    private func glowRadius() -> CGFloat { CGFloat(20 * glowIntensity) }
    private func glowOpacity() -> Double { min(0.9, max(0, glowIntensity)) }
    
    private func measure(text: String, font: UIFont) -> CGSize {
        let attributes = [NSAttributedString.Key.font: font]
        return (text as NSString).size(withAttributes: attributes)
    }
}

// MARK: - Top Overlay

private struct TopOverlay: View {
    let showBattery: Bool
    let showDate: Bool
    let color: Color
    let overallOpacity: Double
    
    @State private var batteryLevel: Int = 100
    @State private var dateString: String = ""
    
    // Update timers
    @State private var minuteTimer: Timer.TimerPublisher? = nil
    @State private var minuteCancellable: AnyCancellable? = nil
    
    private static let dateDF: DateFormatter = {
        let df = DateFormatter()
        df.dateFormat = "d MMMM EEE"
        return df
    }()
    
    var body: some View {
        let clamped = max(0.0, min(overallOpacity, 1.0))
        HStack(spacing: 16) {
            if showBattery {
                Label("\(batteryLevel)%", systemImage: "bolt.circle")
                    .foregroundColor(color)
                    .opacity(clamped)
            }
            if showDate {
                Text(dateString)
                    .foregroundColor(color)
                    .opacity(clamped)
            }
            Spacer()
        }
        .font(.callout.weight(.semibold))
        .padding(.horizontal, 12)
        .padding(.vertical, 8)
        .background(.black.opacity(0.25 * clamped), in: RoundedRectangle(cornerRadius: 16, style: .continuous))
        .overlay(
            RoundedRectangle(cornerRadius: 16).stroke(.white.opacity(0.05 * clamped), lineWidth: 1)
        )
        .onAppear {
            // Initialize date immediately and start minute updates
            updateDate()
            startMinuteUpdates()
            // Initialize battery and start monitoring (UIKit only)
            enableBatteryMonitoring()
            updateBattery()
            startBatteryObserver()
        }
        .onDisappear {
            stopMinuteUpdates()
            stopBatteryObserver()
        }
    }
    
    // MARK: - Date updates
    private func startMinuteUpdates() {
        let pub = Timer.publish(every: 60, on: .main, in: .common)
        minuteTimer = pub
        minuteCancellable = pub.autoconnect().sink { _ in
            updateDate()
        }
    }
    private func stopMinuteUpdates() {
        minuteCancellable?.cancel()
        minuteCancellable = nil
        minuteTimer = nil
    }
    private func updateDate() {
        dateString = Self.dateDF.string(from: Date())
    }
    
    // MARK: - Battery updates
    private func enableBatteryMonitoring() {
        #if canImport(UIKit)
        UIDevice.current.isBatteryMonitoringEnabled = true
        #endif
    }
    private func updateBattery() {
        #if canImport(UIKit)
        let lvl = UIDevice.current.batteryLevel
        if lvl >= 0 {
            batteryLevel = Int((lvl * 100).rounded())
        } else {
            // Unknown battery, keep previous or show 100 (default)
        }
        #endif
    }
    private func startBatteryObserver() {
        #if canImport(UIKit)
        NotificationCenter.default.addObserver(forName: UIDevice.batteryLevelDidChangeNotification, object: nil, queue: .main) { _ in
            updateBattery()
        }
        NotificationCenter.default.addObserver(forName: UIDevice.batteryStateDidChangeNotification, object: nil, queue: .main) { _ in
            updateBattery()
        }
        #endif
    }
    private func stopBatteryObserver() {
        #if canImport(UIKit)
        NotificationCenter.default.removeObserver(self, name: UIDevice.batteryLevelDidChangeNotification, object: nil)
        NotificationCenter.default.removeObserver(self, name: UIDevice.batteryStateDidChangeNotification, object: nil)
        #endif
    }
}
