import Foundation

/// Synor ZK SDK Types for Swift
///
/// Zero-Knowledge proof systems for ZK-Rollups and privacy.

/// Proof system backends
public enum ProofSystem: String, Codable {
    case groth16
    case plonk
    case stark
}

/// Circuit types for different operations
public enum CircuitType: String, Codable {
    case transfer
    case batch
    case deposit
    case withdraw
}

/// Rollup state
public enum RollupState: String, Codable {
    case active
    case paused
    case finalizing
    case finalized
}

/// Proof status
public enum ProofStatus: String, Codable {
    case generating
    case completed
    case failed
    case verified
}

/// Zero-knowledge proof
public struct Proof {
    public let id: String
    public let system: ProofSystem
    public let circuitType: CircuitType
    public let data: String
    public let publicInputs: [String]
    public let size: Int
    public let generationTimeMs: Int
    public let createdAt: Int

    public init(id: String, system: ProofSystem, circuitType: CircuitType, data: String,
                publicInputs: [String], size: Int, generationTimeMs: Int, createdAt: Int) {
        self.id = id
        self.system = system
        self.circuitType = circuitType
        self.data = data
        self.publicInputs = publicInputs
        self.size = size
        self.generationTimeMs = generationTimeMs
        self.createdAt = createdAt
    }

    public static func fromJson(_ json: [String: Any]) throws -> Proof {
        guard let id = json["id"] as? String,
              let systemStr = json["system"] as? String,
              let system = ProofSystem(rawValue: systemStr),
              let circuitTypeStr = json["circuit_type"] as? String,
              let circuitType = CircuitType(rawValue: circuitTypeStr),
              let data = json["data"] as? String else {
            throw ZkError("Invalid proof JSON")
        }
        return Proof(
            id: id,
            system: system,
            circuitType: circuitType,
            data: data,
            publicInputs: json["public_inputs"] as? [String] ?? [],
            size: json["size"] as? Int ?? 0,
            generationTimeMs: json["generation_time_ms"] as? Int ?? 0,
            createdAt: json["created_at"] as? Int ?? 0
        )
    }
}

/// Verification key for a circuit
public struct VerificationKey {
    public let circuitId: String
    public let circuitType: CircuitType
    public let system: ProofSystem
    public let data: String
    public let size: Int

    public static func fromJson(_ json: [String: Any]) throws -> VerificationKey {
        guard let circuitId = json["circuit_id"] as? String,
              let circuitTypeStr = json["circuit_type"] as? String,
              let circuitType = CircuitType(rawValue: circuitTypeStr),
              let systemStr = json["system"] as? String,
              let system = ProofSystem(rawValue: systemStr),
              let data = json["data"] as? String else {
            throw ZkError("Invalid verification key JSON")
        }
        return VerificationKey(
            circuitId: circuitId,
            circuitType: circuitType,
            system: system,
            data: data,
            size: json["size"] as? Int ?? 0
        )
    }
}

/// Proving key for generating proofs
public struct ProvingKey {
    public let circuitId: String
    public let circuitType: CircuitType
    public let system: ProofSystem
    public let data: String
    public let size: Int

    public static func fromJson(_ json: [String: Any]) throws -> ProvingKey {
        guard let circuitId = json["circuit_id"] as? String,
              let circuitTypeStr = json["circuit_type"] as? String,
              let circuitType = CircuitType(rawValue: circuitTypeStr),
              let systemStr = json["system"] as? String,
              let system = ProofSystem(rawValue: systemStr),
              let data = json["data"] as? String else {
            throw ZkError("Invalid proving key JSON")
        }
        return ProvingKey(
            circuitId: circuitId,
            circuitType: circuitType,
            system: system,
            data: data,
            size: json["size"] as? Int ?? 0
        )
    }
}

/// Circuit configuration
public struct CircuitConfig {
    public let type: CircuitType
    public let maxBatchSize: Int?
    public let treeDepth: Int?
    public let verifySignatures: Bool?
    public let customConstraints: [String: Any]?

    public static func fromJson(_ json: [String: Any]) -> CircuitConfig? {
        guard let typeStr = json["type"] as? String,
              let type = CircuitType(rawValue: typeStr) else { return nil }
        return CircuitConfig(
            type: type,
            maxBatchSize: json["max_batch_size"] as? Int,
            treeDepth: json["tree_depth"] as? Int,
            verifySignatures: json["verify_signatures"] as? Bool,
            customConstraints: json["custom_constraints"] as? [String: Any]
        )
    }

    public func toJson() -> [String: Any] {
        var result: [String: Any] = ["type": type.rawValue]
        if let v = maxBatchSize { result["max_batch_size"] = v }
        if let v = treeDepth { result["tree_depth"] = v }
        if let v = verifySignatures { result["verify_signatures"] = v }
        if let v = customConstraints { result["custom_constraints"] = v }
        return result
    }
}

/// Account state in the rollup
public struct AccountState {
    public let address: String
    public let balance: String
    public let nonce: Int
    public let pubkeyHash: String

    public static func fromJson(_ json: [String: Any]) -> AccountState {
        AccountState(
            address: json["address"] as? String ?? "",
            balance: json["balance"] as? String ?? "0",
            nonce: json["nonce"] as? Int ?? 0,
            pubkeyHash: json["pubkey_hash"] as? String ?? ""
        )
    }
}

/// Transfer operation
public struct Transfer {
    public let from: String
    public let to: String
    public let amount: String
    public let fee: String
    public let nonce: Int
    public let signature: String?

    public init(from: String, to: String, amount: String, fee: String, nonce: Int, signature: String? = nil) {
        self.from = from
        self.to = to
        self.amount = amount
        self.fee = fee
        self.nonce = nonce
        self.signature = signature
    }

    public static func fromJson(_ json: [String: Any]) -> Transfer? {
        guard let from = json["from"] as? String,
              let to = json["to"] as? String,
              let amount = json["amount"] as? String,
              let fee = json["fee"] as? String else { return nil }
        return Transfer(
            from: from,
            to: to,
            amount: amount,
            fee: fee,
            nonce: json["nonce"] as? Int ?? 0,
            signature: json["signature"] as? String
        )
    }

    public func toJson() -> [String: Any] {
        var result: [String: Any] = [
            "from": from,
            "to": to,
            "amount": amount,
            "fee": fee,
            "nonce": nonce
        ]
        if let sig = signature { result["signature"] = sig }
        return result
    }
}

/// Deposit operation (L1 -> L2)
public struct Deposit {
    public let l1TxHash: String
    public let recipient: String
    public let amount: String
    public let token: String?

    public init(l1TxHash: String, recipient: String, amount: String, token: String? = nil) {
        self.l1TxHash = l1TxHash
        self.recipient = recipient
        self.amount = amount
        self.token = token
    }

    public func toJson() -> [String: Any] {
        var result: [String: Any] = [
            "l1_tx_hash": l1TxHash,
            "recipient": recipient,
            "amount": amount
        ]
        if let t = token { result["token"] = t }
        return result
    }
}

/// Withdrawal operation (L2 -> L1)
public struct Withdrawal {
    public let sender: String
    public let recipient: String
    public let amount: String
    public let token: String?
    public let nonce: Int
    public let signature: String?

    public init(sender: String, recipient: String, amount: String, token: String? = nil, nonce: Int, signature: String? = nil) {
        self.sender = sender
        self.recipient = recipient
        self.amount = amount
        self.token = token
        self.nonce = nonce
        self.signature = signature
    }

    public func toJson() -> [String: Any] {
        var result: [String: Any] = [
            "sender": sender,
            "recipient": recipient,
            "amount": amount,
            "nonce": nonce
        ]
        if let t = token { result["token"] = t }
        if let sig = signature { result["signature"] = sig }
        return result
    }
}

/// Rollup batch
public struct Batch {
    public let batchNumber: Int
    public let prevStateRoot: String
    public let newStateRoot: String
    public let transfers: [Transfer]
    public let deposits: [Deposit]
    public let withdrawals: [Withdrawal]
    public let proof: Proof?
    public let timestamp: Int

    public static func fromJson(_ json: [String: Any]) throws -> Batch {
        let transfers = (json["transfers"] as? [[String: Any]] ?? []).compactMap { Transfer.fromJson($0) }
        return Batch(
            batchNumber: json["batch_number"] as? Int ?? 0,
            prevStateRoot: json["prev_state_root"] as? String ?? "",
            newStateRoot: json["new_state_root"] as? String ?? "",
            transfers: transfers,
            deposits: [],
            withdrawals: [],
            proof: (json["proof"] as? [String: Any]).flatMap { try? Proof.fromJson($0) },
            timestamp: json["timestamp"] as? Int ?? 0
        )
    }
}

/// Rollup statistics
public struct RollupStats {
    public let currentBatch: Int
    public let totalTransactions: Int
    public let totalProofs: Int
    public let avgProofTimeMs: Int
    public let stateRoot: String
    public let accountCount: Int
    public let tvl: String

    public static func fromJson(_ json: [String: Any]) -> RollupStats {
        RollupStats(
            currentBatch: json["current_batch"] as? Int ?? 0,
            totalTransactions: json["total_transactions"] as? Int ?? 0,
            totalProofs: json["total_proofs"] as? Int ?? 0,
            avgProofTimeMs: json["avg_proof_time_ms"] as? Int ?? 0,
            stateRoot: json["state_root"] as? String ?? "",
            accountCount: json["account_count"] as? Int ?? 0,
            tvl: json["tvl"] as? String ?? "0"
        )
    }
}

/// Proof generation request
public struct ProofRequest {
    public let circuitType: CircuitType
    public let publicInputs: [String]
    public let privateInputs: [String]
    public let system: ProofSystem?

    public init(circuitType: CircuitType, publicInputs: [String], privateInputs: [String], system: ProofSystem? = nil) {
        self.circuitType = circuitType
        self.publicInputs = publicInputs
        self.privateInputs = privateInputs
        self.system = system
    }
}

/// Proof verification result
public struct VerificationResult {
    public let valid: Bool
    public let verificationTimeMs: Int
    public let error: String?

    public static func fromJson(_ json: [String: Any]) -> VerificationResult {
        VerificationResult(
            valid: json["valid"] as? Bool ?? false,
            verificationTimeMs: json["verification_time_ms"] as? Int ?? 0,
            error: json["error"] as? String
        )
    }
}

/// Merkle proof for state inclusion
public struct MerkleProof {
    public let leaf: String
    public let path: [String]
    public let indices: [Int]
    public let root: String

    public static func fromJson(_ json: [String: Any]) -> MerkleProof {
        MerkleProof(
            leaf: json["leaf"] as? String ?? "",
            path: json["path"] as? [String] ?? [],
            indices: json["indices"] as? [Int] ?? [],
            root: json["root"] as? String ?? ""
        )
    }

    public func toJson() -> [String: Any] {
        ["leaf": leaf, "path": path, "indices": indices, "root": root]
    }
}

/// Trusted setup ceremony contribution
public struct CeremonyContribution {
    public let contributorId: String
    public let hash: String
    public let timestamp: Int
    public let verified: Bool

    public static func fromJson(_ json: [String: Any]) -> CeremonyContribution {
        CeremonyContribution(
            contributorId: json["contributor_id"] as? String ?? "",
            hash: json["hash"] as? String ?? "",
            timestamp: json["timestamp"] as? Int ?? 0,
            verified: json["verified"] as? Bool ?? false
        )
    }
}

/// Trusted setup ceremony
public struct TrustedSetup {
    public let id: String
    public let circuitType: CircuitType
    public let system: ProofSystem
    public let contributionCount: Int
    public let latestHash: String
    public let complete: Bool
    public let contributions: [CeremonyContribution]

    public static func fromJson(_ json: [String: Any]) -> TrustedSetup {
        let contributions = (json["contributions"] as? [[String: Any]] ?? []).map { CeremonyContribution.fromJson($0) }
        return TrustedSetup(
            id: json["id"] as? String ?? "",
            circuitType: CircuitType(rawValue: json["circuit_type"] as? String ?? "transfer") ?? .transfer,
            system: ProofSystem(rawValue: json["system"] as? String ?? "groth16") ?? .groth16,
            contributionCount: json["contribution_count"] as? Int ?? 0,
            latestHash: json["latest_hash"] as? String ?? "",
            complete: json["complete"] as? Bool ?? false,
            contributions: contributions
        )
    }
}

/// ZK SDK configuration
public struct ZkConfig {
    public let apiKey: String
    public let endpoint: String
    public let wsEndpoint: String
    public let timeout: Int
    public let retries: Int
    public let defaultProofSystem: ProofSystem
    public let debug: Bool

    public init(
        apiKey: String,
        endpoint: String = "https://zk.synor.io/v1",
        wsEndpoint: String = "wss://zk.synor.io/v1/ws",
        timeout: Int = 60,
        retries: Int = 3,
        defaultProofSystem: ProofSystem = .groth16,
        debug: Bool = false
    ) {
        self.apiKey = apiKey
        self.endpoint = endpoint
        self.wsEndpoint = wsEndpoint
        self.timeout = timeout
        self.retries = retries
        self.defaultProofSystem = defaultProofSystem
        self.debug = debug
    }
}

/// ZK SDK error
public struct ZkError: Error, CustomStringConvertible {
    public let message: String
    public let code: String?
    public let status: Int?

    public init(_ message: String, code: String? = nil, status: Int? = nil) {
        self.message = message
        self.code = code
        self.status = status
    }

    public var description: String {
        "ZkError: \(message)\(code.map { " (\($0))" } ?? "")"
    }
}

/// Proof system characteristics
public struct ProofSystemInfo {
    public let name: String
    public let proofSize: Int
    public let verificationTimeMs: Int
    public let trustedSetup: Bool
    public let universal: Bool
}

public func getProofSystemInfo(_ system: ProofSystem) -> ProofSystemInfo {
    switch system {
    case .groth16:
        return ProofSystemInfo(name: "Groth16", proofSize: 192, verificationTimeMs: 10, trustedSetup: true, universal: false)
    case .plonk:
        return ProofSystemInfo(name: "PLONK", proofSize: 512, verificationTimeMs: 15, trustedSetup: true, universal: true)
    case .stark:
        return ProofSystemInfo(name: "STARK", proofSize: 50000, verificationTimeMs: 30, trustedSetup: false, universal: true)
    }
}

// Constants
public let MAX_BATCH_SIZE = 1000
public let STATE_TREE_DEPTH = 32
public let PROOF_EXPIRY_BLOCKS = 100
