/**
 * iOS Consent SDK - ConsentManager
 *
 * DSGVO/TTDSG-konformes Consent Management fuer iOS Apps.
 *
 * Nutzung:
 * 1. Im AppDelegate/App.init() konfigurieren
 * 2. In SwiftUI Views mit @EnvironmentObject nutzen
 * 3. Banner mit .consentBanner() Modifier anzeigen
 *
 * Copyright (c) 2025 BreakPilot
 * Apache License 2.0
 */

import Foundation
import SwiftUI
import Combine
import CryptoKit

// MARK: - Consent Categories

/// Standard-Consent-Kategorien nach IAB TCF 2.2
public enum ConsentCategory: String, CaseIterable, Codable {
    case essential     // Technisch notwendig
    case functional    // Personalisierung
    case analytics     // Nutzungsanalyse
    case marketing     // Werbung
    case social        // Social Media
}

// MARK: - Consent State

/// Aktueller Consent-Zustand
public struct ConsentState: Codable, Equatable {
    public var categories: [ConsentCategory: Bool]
    public var vendors: [String: Bool]
    public var timestamp: Date
    public var version: String
    public var consentId: String?
    public var expiresAt: Date?
    public var tcfString: String?

    public init(
        categories: [ConsentCategory: Bool] = [:],
        vendors: [String: Bool] = [:],
        timestamp: Date = Date(),
        version: String = "1.0.0"
    ) {
        self.categories = categories
        self.vendors = vendors
        self.timestamp = timestamp
        self.version = version
    }

    /// Default State mit nur essential = true
    public static var `default`: ConsentState {
        ConsentState(
            categories: [
                .essential: true,
                .functional: false,
                .analytics: false,
                .marketing: false,
                .social: false
            ]
        )
    }
}

// MARK: - Configuration

/// SDK-Konfiguration
public struct ConsentConfig {
    public let apiEndpoint: String
    public let siteId: String
    public var language: String = Locale.current.language.languageCode?.identifier ?? "en"
    public var showRejectAll: Bool = true
    public var showAcceptAll: Bool = true
    public var granularControl: Bool = true
    public var rememberDays: Int = 365
    public var debug: Bool = false

    public init(apiEndpoint: String, siteId: String) {
        self.apiEndpoint = apiEndpoint
        self.siteId = siteId
    }
}

// MARK: - Consent Manager

/// Haupt-Manager fuer Consent-Verwaltung
@MainActor
public final class ConsentManager: ObservableObject {

    // MARK: Singleton

    public static let shared = ConsentManager()

    // MARK: Published Properties

    @Published public private(set) var consent: ConsentState = .default
    @Published public private(set) var isInitialized: Bool = false
    @Published public private(set) var isLoading: Bool = true
    @Published public private(set) var isBannerVisible: Bool = false
    @Published public private(set) var isSettingsVisible: Bool = false

    // MARK: Private Properties

    private var config: ConsentConfig?
    private var storage: ConsentStorage?
    private var apiClient: ConsentAPIClient?
    private var cancellables = Set<AnyCancellable>()

    // MARK: - Initialization

    private init() {}

    /// Konfiguriert den ConsentManager
    public func configure(_ config: ConsentConfig) {
        self.config = config
        self.storage = ConsentStorage()
        self.apiClient = ConsentAPIClient(
            baseURL: config.apiEndpoint,
            siteId: config.siteId
        )

        if config.debug {
            print("[ConsentSDK] Configured with siteId: \(config.siteId)")
        }

        Task {
            await initialize()
        }
    }

    /// Initialisiert und laedt gespeicherten Consent
    private func initialize() async {
        defer { isLoading = false }

        // Lokalen Consent laden
        if let stored = storage?.load() {
            consent = stored

            // Pruefen ob abgelaufen
            if let expiresAt = stored.expiresAt, Date() > expiresAt {
                consent = .default
                storage?.clear()
            }
        }

        // Vom Server synchronisieren (optional)
        do {
            if let serverConsent = try await apiClient?.getConsent(
                fingerprint: DeviceFingerprint.generate()
            ) {
                consent = serverConsent
                storage?.save(consent)
            }
        } catch {
            if config?.debug == true {
                print("[ConsentSDK] Failed to sync consent: \(error)")
            }
        }

        isInitialized = true

        // Banner anzeigen falls noetig
        if needsConsent {
            showBanner()
        }
    }

    // MARK: - Public API

    /// Prueft ob Consent fuer Kategorie erteilt wurde
    public func hasConsent(_ category: ConsentCategory) -> Bool {
        // Essential ist immer erlaubt
        if category == .essential { return true }
        return consent.categories[category] ?? false
    }

    /// Prueft ob Consent eingeholt werden muss
    public var needsConsent: Bool {
        consent.consentId == nil
    }

    /// Alle Kategorien akzeptieren
    public func acceptAll() async {
        var newConsent = consent
        for category in ConsentCategory.allCases {
            newConsent.categories[category] = true
        }
        newConsent.timestamp = Date()

        await saveConsent(newConsent)
        hideBanner()
    }

    /// Alle nicht-essentiellen Kategorien ablehnen
    public func rejectAll() async {
        var newConsent = consent
        for category in ConsentCategory.allCases {
            newConsent.categories[category] = category == .essential
        }
        newConsent.timestamp = Date()

        await saveConsent(newConsent)
        hideBanner()
    }

    /// Auswahl speichern
    public func saveSelection(_ categories: [ConsentCategory: Bool]) async {
        var newConsent = consent
        newConsent.categories = categories
        newConsent.categories[.essential] = true // Essential immer true
        newConsent.timestamp = Date()

        await saveConsent(newConsent)
        hideBanner()
    }

    // MARK: - UI Control

    /// Banner anzeigen
    public func showBanner() {
        isBannerVisible = true
    }

    /// Banner ausblenden
    public func hideBanner() {
        isBannerVisible = false
    }

    /// Einstellungen anzeigen
    public func showSettings() {
        isSettingsVisible = true
    }

    /// Einstellungen ausblenden
    public func hideSettings() {
        isSettingsVisible = false
    }

    // MARK: - Private Methods

    private func saveConsent(_ newConsent: ConsentState) async {
        // Lokal speichern
        storage?.save(newConsent)

        // An Server senden
        do {
            let response = try await apiClient?.saveConsent(
                consent: newConsent,
                fingerprint: DeviceFingerprint.generate()
            )
            var updated = newConsent
            updated.consentId = response?.consentId
            updated.expiresAt = response?.expiresAt
            consent = updated
            storage?.save(updated)
        } catch {
            // Lokal speichern auch bei Fehler
            consent = newConsent
            if config?.debug == true {
                print("[ConsentSDK] Failed to sync consent: \(error)")
            }
        }
    }
}

// MARK: - Storage

/// Sichere Speicherung im Keychain
final class ConsentStorage {
    private let key = "com.breakpilot.consent.state"

    func load() -> ConsentState? {
        guard let data = KeychainHelper.read(key: key) else { return nil }
        return try? JSONDecoder().decode(ConsentState.self, from: data)
    }

    func save(_ consent: ConsentState) {
        guard let data = try? JSONEncoder().encode(consent) else { return }
        KeychainHelper.write(data: data, key: key)
    }

    func clear() {
        KeychainHelper.delete(key: key)
    }
}

/// Keychain Helper
enum KeychainHelper {
    static func write(data: Data, key: String) {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key,
            kSecValueData as String: data,
            kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock
        ]
        SecItemDelete(query as CFDictionary)
        SecItemAdd(query as CFDictionary, nil)
    }

    static func read(key: String) -> Data? {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key,
            kSecReturnData as String: true
        ]
        var result: AnyObject?
        SecItemCopyMatching(query as CFDictionary, &result)
        return result as? Data
    }

    static func delete(key: String) {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key
        ]
        SecItemDelete(query as CFDictionary)
    }
}

// MARK: - API Client

/// API Client fuer Backend-Kommunikation
final class ConsentAPIClient {
    private let baseURL: String
    private let siteId: String

    init(baseURL: String, siteId: String) {
        self.baseURL = baseURL
        self.siteId = siteId
    }

    struct ConsentResponse: Codable {
        let consentId: String
        let expiresAt: Date
    }

    func getConsent(fingerprint: String) async throws -> ConsentState? {
        let url = URL(string: "\(baseURL)/banner/consent?site_id=\(siteId)&fingerprint=\(fingerprint)")!
        let (data, response) = try await URLSession.shared.data(from: url)

        guard let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            return nil
        }

        return try JSONDecoder().decode(ConsentState.self, from: data)
    }

    func saveConsent(consent: ConsentState, fingerprint: String) async throws -> ConsentResponse {
        var request = URLRequest(url: URL(string: "\(baseURL)/banner/consent")!)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")

        let body: [String: Any] = [
            "site_id": siteId,
            "device_fingerprint": fingerprint,
            "categories": Dictionary(
                uniqueKeysWithValues: consent.categories.map { ($0.key.rawValue, $0.value) }
            ),
            "vendors": consent.vendors,
            "platform": "ios",
            "app_version": Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0.0"
        ]
        request.httpBody = try JSONSerialization.data(withJSONObject: body)

        let (data, _) = try await URLSession.shared.data(for: request)
        return try JSONDecoder().decode(ConsentResponse.self, from: data)
    }
}

// MARK: - Device Fingerprint

/// Privacy-konformer Device Fingerprint
enum DeviceFingerprint {
    static func generate() -> String {
        // Vendor ID (reset-safe)
        let vendorId = UIDevice.current.identifierForVendor?.uuidString ?? UUID().uuidString

        // System Info
        let model = UIDevice.current.model
        let systemVersion = UIDevice.current.systemVersion
        let locale = Locale.current.identifier

        // Hash erstellen
        let raw = "\(vendorId)-\(model)-\(systemVersion)-\(locale)"
        let data = Data(raw.utf8)
        let hash = SHA256.hash(data: data)
        return hash.compactMap { String(format: "%02x", $0) }.joined()
    }
}

// MARK: - SwiftUI Extensions

/// Environment Key fuer ConsentManager
private struct ConsentManagerKey: EnvironmentKey {
    static let defaultValue = ConsentManager.shared
}

extension EnvironmentValues {
    public var consentManager: ConsentManager {
        get { self[ConsentManagerKey.self] }
        set { self[ConsentManagerKey.self] = newValue }
    }
}

/// Banner ViewModifier
public struct ConsentBannerModifier: ViewModifier {
    @ObservedObject var consent = ConsentManager.shared

    public func body(content: Content) -> some View {
        ZStack {
            content

            if consent.isBannerVisible {
                ConsentBannerView()
            }
        }
    }
}

extension View {
    /// Fuegt einen Consent-Banner hinzu
    public func consentBanner() -> some View {
        modifier(ConsentBannerModifier())
    }
}

// MARK: - Banner View

/// Default Consent Banner UI
public struct ConsentBannerView: View {
    @ObservedObject var consent = ConsentManager.shared

    public init() {}

    public var body: some View {
        VStack(spacing: 0) {
            Spacer()

            VStack(spacing: 16) {
                Text("Datenschutzeinstellungen")
                    .font(.headline)

                Text("Wir nutzen Cookies und ähnliche Technologien, um Ihnen ein optimales Nutzererlebnis zu bieten.")
                    .font(.subheadline)
                    .foregroundStyle(.secondary)
                    .multilineTextAlignment(.center)

                HStack(spacing: 12) {
                    Button("Alle ablehnen") {
                        Task { await consent.rejectAll() }
                    }
                    .buttonStyle(.bordered)

                    Button("Einstellungen") {
                        consent.showSettings()
                    }
                    .buttonStyle(.bordered)

                    Button("Alle akzeptieren") {
                        Task { await consent.acceptAll() }
                    }
                    .buttonStyle(.borderedProminent)
                }
            }
            .padding(24)
            .background(.regularMaterial)
            .clipShape(RoundedRectangle(cornerRadius: 24, style: .continuous))
            .padding()
            .shadow(radius: 20)
        }
        .transition(.move(edge: .bottom).combined(with: .opacity))
        .animation(.spring(), value: consent.isBannerVisible)
    }
}

// MARK: - Consent Gate

/// Zeigt Inhalt nur bei Consent an
public struct ConsentGate<Content: View, Placeholder: View>: View {
    let category: ConsentCategory
    let content: () -> Content
    let placeholder: () -> Placeholder

    @ObservedObject var consent = ConsentManager.shared

    public init(
        category: ConsentCategory,
        @ViewBuilder content: @escaping () -> Content,
        @ViewBuilder placeholder: @escaping () -> Placeholder
    ) {
        self.category = category
        self.content = content
        self.placeholder = placeholder
    }

    public var body: some View {
        if consent.hasConsent(category) {
            content()
        } else {
            placeholder()
        }
    }
}

extension ConsentGate where Placeholder == EmptyView {
    public init(
        category: ConsentCategory,
        @ViewBuilder content: @escaping () -> Content
    ) {
        self.init(category: category, content: content, placeholder: { EmptyView() })
    }
}
