import ClientTLS
import Foundation
import Observation
import SwiftUI
import UniformTypeIdentifiers

/// C-iOS-3 · Device-certificate install / rotation screen.
///
/// Flow: `.fileImporter([.pkcs12])` → secure passphrase → import (validates via
/// `SecPKCS12Import`) → persist in the keychain → show issuer CN + expiry, with
/// replace/rotate + remove. This is the in-app, app-sandboxed install path (a
/// `.p12` delivered via AirDrop/Files), NOT a configuration profile.
///
/// INTEGRATION NOTE: presenting this screen needs a one-line nav/Settings entry
/// ("Device certificate") in the app chrome (RootView / PairingScreen), which
/// live outside this task's ownership. The screen is fully self-contained so
/// that wiring is a single `NavigationLink { ClientCertScreen() }`.
struct ClientCertScreen: View {
    @State private var model: ClientCertViewModel

    init(model: ClientCertViewModel = ClientCertViewModel()) {
        _model = State(initialValue: model)
    }

    var body: some View {
        Form {
            installedSection
            importSection
            if model.summary != nil {
                removeSection
            }
        }
        .navigationTitle(ClientCertCopy.title)
        .fileImporter(
            isPresented: $model.isPickingFile,
            allowedContentTypes: [.pkcs12],
            allowsMultipleSelection: false
        ) { result in
            model.handleFileSelection(result)
        }
        .task { model.refresh() }
    }

    // MARK: - Sections

    @ViewBuilder private var installedSection: some View {
        Section(ClientCertCopy.installedHeader) {
            if let summary = model.summary {
                LabeledContent(ClientCertCopy.subject, value: summary.subjectCommonName ?? "—")
                LabeledContent(ClientCertCopy.issuer, value: summary.issuerCommonName ?? "—")
                LabeledContent(ClientCertCopy.expiry, value: model.expiryText(summary))
                if summary.isExpired() {
                    Label(ClientCertCopy.expiredWarning, systemImage: "exclamationmark.triangle")
                        .foregroundStyle(.orange)
                }
            } else {
                Text(ClientCertCopy.noneInstalled).foregroundStyle(.secondary)
            }
        }
    }

    @ViewBuilder private var importSection: some View {
        Section {
            if let name = model.pendingFileName {
                LabeledContent(ClientCertCopy.selectedFile, value: name)
                SecureField(ClientCertCopy.passphrase, text: $model.passphrase)
                    .textContentType(.password)
                    .autocorrectionDisabled()
                Button(ClientCertCopy.importAction) { model.importPending() }
                    .disabled(!model.canImport)
                Button(ClientCertCopy.cancelAction, role: .cancel) { model.clearPending() }
            } else {
                Button {
                    model.beginPicking()
                } label: {
                    Label(
                        model.summary == nil
                            ? ClientCertCopy.chooseFile
                            : ClientCertCopy.replaceFile,
                        systemImage: "doc.badge.plus"
                    )
                }
            }
            if let error = model.errorMessage {
                Text(error).foregroundStyle(.red).font(.footnote)
            }
        } header: {
            Text(model.summary == nil ? ClientCertCopy.importHeader : ClientCertCopy.rotateHeader)
        } footer: {
            Text(ClientCertCopy.importFooter)
        }
    }

    @ViewBuilder private var removeSection: some View {
        Section {
            Button(ClientCertCopy.removeAction, role: .destructive) { model.remove() }
        }
    }
}

/// State machine for the install screen. Errors are mapped to actionable copy
/// and never swallowed; a failed import leaves any prior identity intact.
@MainActor
@Observable
final class ClientCertViewModel {
    private(set) var summary: ClientCertificateSummary?
    var isPickingFile = false
    var passphrase = ""
    private(set) var pendingData: Data?
    private(set) var pendingFileName: String?
    private(set) var errorMessage: String?

    @ObservationIgnored private let store: any ClientIdentityStore

    init(store: any ClientIdentityStore = KeychainClientIdentityStore()) {
        self.store = store
    }

    var canImport: Bool { pendingData != nil && !passphrase.isEmpty }

    func refresh() {
        do {
            summary = try store.loadSummary()
        } catch {
            summary = nil
            errorMessage = ClientCertCopy.loadFailed
        }
    }

    func beginPicking() {
        errorMessage = nil
        isPickingFile = true
    }

    func handleFileSelection(_ result: Result<[URL], Error>) {
        switch result {
        case .failure:
            errorMessage = ClientCertCopy.fileReadFailed
        case .success(let urls):
            guard let url = urls.first else { return }
            readFile(at: url)
        }
    }

    private func readFile(at url: URL) {
        let didAccess = url.startAccessingSecurityScopedResource()
        defer { if didAccess { url.stopAccessingSecurityScopedResource() } }
        do {
            pendingData = try Data(contentsOf: url)
            pendingFileName = url.lastPathComponent
            passphrase = ""
            errorMessage = nil
        } catch {
            pendingData = nil
            pendingFileName = nil
            errorMessage = ClientCertCopy.fileReadFailed
        }
    }

    func importPending() {
        guard let data = pendingData else { return }
        do {
            try store.save(p12Data: data, passphrase: passphrase)
            clearPending()
            refresh()
        } catch {
            errorMessage = Self.copy(for: error)
        }
    }

    func clearPending() {
        pendingData = nil
        pendingFileName = nil
        passphrase = ""
        errorMessage = nil
    }

    func remove() {
        do {
            try store.remove()
            summary = nil
            errorMessage = nil
        } catch {
            errorMessage = ClientCertCopy.removeFailed
        }
    }

    func expiryText(_ summary: ClientCertificateSummary) -> String {
        guard let notAfter = summary.notAfter else { return "—" }
        return notAfter.formatted(date: .abbreviated, time: .omitted)
    }

    /// Map an import/storage error to install-UX copy (never swallowed).
    private static func copy(for error: any Error) -> String {
        switch error {
        case PKCS12ImportError.wrongPassphrase:
            return ClientCertCopy.errWrongPassphrase
        case PKCS12ImportError.corruptFile:
            return ClientCertCopy.errCorruptFile
        case PKCS12ImportError.unsupported:
            return ClientCertCopy.errUnsupported
        case PKCS12ImportError.noIdentity:
            return ClientCertCopy.errNoIdentity
        case ClientIdentityStoreError.keychain, ClientIdentityStoreError.corruptStoredBlob:
            return ClientCertCopy.errKeychain
        default:
            return ClientCertCopy.errUnknown
        }
    }
}

/// User-facing copy (Chinese, matching the rest of the app).
enum ClientCertCopy {
    static let title = "设备证书"
    static let installedHeader = "已安装证书"
    static let subject = "设备 (CN)"
    static let issuer = "签发 CA"
    static let expiry = "有效期至"
    static let expiredWarning = "证书已过期，请重新导入。"
    static let noneInstalled = "尚未安装设备证书。"

    static let importHeader = "导入证书"
    static let rotateHeader = "替换证书"
    static let chooseFile = "选择 .p12 文件"
    static let replaceFile = "选择新的 .p12 文件"
    static let selectedFile = "已选文件"
    static let passphrase = "证书密码"
    static let importAction = "导入"
    static let cancelAction = "取消"
    static let removeAction = "移除证书"
    static let importFooter =
        "证书用于连接你自己的隧道主机 (mTLS)。通过 AirDrop / 文件 把 .p12 传到本机后在此导入；证书与密码只保存在本设备钥匙串。"

    static let errWrongPassphrase = "密码错误，请重试。"
    static let errCorruptFile = "文件无法识别，请确认是有效的 .p12 证书。"
    static let errUnsupported = "证书格式不受支持（请用 openssl pkcs12 -export -legacy 生成）。"
    static let errNoIdentity = "该 .p12 不含身份（缺少私钥），无法用于客户端认证。"
    static let errKeychain = "保存到钥匙串失败，请重试。"
    static let errUnknown = "导入失败，请重试。"
    static let loadFailed = "读取已安装证书失败。"
    static let fileReadFailed = "无法读取所选文件。"
    static let removeFailed = "移除证书失败，请重试。"
}
