import Foundation

// MARK: - Enums

/// Network environment for wallet operations.
public enum Network: String, Codable {
    case mainnet
    case testnet
    case devnet
}

/// Type of wallet to create.
public enum WalletType: String, Codable {
    case standard
    case multisig
    case hardware
    case stealth
}

/// Transaction priority for fee estimation.
public enum Priority: String, Codable {
    case low
    case medium
    case high
}

// MARK: - Configuration

/// Configuration for the wallet client.
public struct WalletConfig {
    public let apiKey: String
    public let endpoint: String
    public let network: Network
    public let timeout: TimeInterval
    public let retries: Int
    public let debug: Bool

    public init(
        apiKey: String,
        endpoint: String = "https://wallet.synor.io/v1",
        network: Network = .mainnet,
        timeout: TimeInterval = 30.0,
        retries: Int = 3,
        debug: Bool = false
    ) {
        self.apiKey = apiKey
        self.endpoint = endpoint
        self.network = network
        self.timeout = timeout
        self.retries = retries
        self.debug = debug
    }
}

// MARK: - Models

/// A Synor wallet with addresses and keys.
public struct Wallet: Codable {
    public let id: String
    public let type: WalletType
    public let network: Network
    public let addresses: [Address]
    public let createdAt: String

    enum CodingKeys: String, CodingKey {
        case id, type, network, addresses
        case createdAt = "created_at"
    }
}

/// A wallet address.
public struct Address: Codable {
    public let address: String
    public let index: Int
    public let isChange: Bool

    enum CodingKeys: String, CodingKey {
        case address, index
        case isChange = "is_change"
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        address = try container.decode(String.self, forKey: .address)
        index = try container.decode(Int.self, forKey: .index)
        isChange = try container.decodeIfPresent(Bool.self, forKey: .isChange) ?? false
    }
}

/// Stealth address for privacy transactions.
public struct StealthAddress: Codable {
    public let spendPublicKey: String
    public let viewPublicKey: String
    public let oneTimeAddress: String

    enum CodingKeys: String, CodingKey {
        case spendPublicKey = "spend_public_key"
        case viewPublicKey = "view_public_key"
        case oneTimeAddress = "one_time_address"
    }
}

/// Result of wallet creation.
public struct CreateWalletResult: Codable {
    public let wallet: Wallet
    public let mnemonic: String?
}

/// A blockchain transaction for signing.
public struct Transaction: Codable {
    public let inputs: [TransactionInput]
    public let outputs: [TransactionOutput]
    public let fee: Int64?
    public let priority: Priority

    public init(
        inputs: [TransactionInput],
        outputs: [TransactionOutput],
        fee: Int64? = nil,
        priority: Priority = .medium
    ) {
        self.inputs = inputs
        self.outputs = outputs
        self.fee = fee
        self.priority = priority
    }
}

/// Transaction input (UTXO reference).
public struct TransactionInput: Codable {
    public let txid: String
    public let vout: Int
    public let amount: Int64

    public init(txid: String, vout: Int, amount: Int64) {
        self.txid = txid
        self.vout = vout
        self.amount = amount
    }
}

/// Transaction output.
public struct TransactionOutput: Codable {
    public let address: String
    public let amount: Int64

    public init(address: String, amount: Int64) {
        self.address = address
        self.amount = amount
    }
}

/// A signed transaction ready for broadcast.
public struct SignedTransaction: Codable {
    public let txid: String
    public let hex: String
    public let size: Int
    public let fee: Int64
}

/// A cryptographic signature.
public struct Signature: Codable {
    public let signature: String
    public let address: String
    public let recoveryId: Int?

    enum CodingKeys: String, CodingKey {
        case signature, address
        case recoveryId = "recovery_id"
    }
}

/// Unspent transaction output.
public struct UTXO: Codable {
    public let txid: String
    public let vout: Int
    public let amount: Int64
    public let address: String
    public let confirmations: Int
    public let scriptPubKey: String?

    enum CodingKeys: String, CodingKey {
        case txid, vout, amount, address, confirmations
        case scriptPubKey = "script_pubkey"
    }
}

/// Wallet balance information.
public struct Balance: Codable {
    public let confirmed: Int64
    public let unconfirmed: Int64
    public let total: Int64
}

// MARK: - Internal Request Types

struct CreateWalletRequest: Codable {
    let type: WalletType
    let network: Network
}

struct ImportWalletRequest: Codable {
    let mnemonic: String
    let passphrase: String?
    let network: Network
}

struct SignTransactionRequest: Codable {
    let walletId: String
    let transaction: Transaction

    enum CodingKeys: String, CodingKey {
        case walletId = "wallet_id"
        case transaction
    }
}

struct SignMessageRequest: Codable {
    let walletId: String
    let message: String
    let addressIndex: Int

    enum CodingKeys: String, CodingKey {
        case walletId = "wallet_id"
        case message
        case addressIndex = "address_index"
    }
}

struct GenerateAddressRequest: Codable {
    let isChange: Bool

    enum CodingKeys: String, CodingKey {
        case isChange = "is_change"
    }
}

struct VerifyMessageRequest: Codable {
    let message: String
    let signature: String
    let address: String
}

struct VerifyMessageResponse: Codable {
    let valid: Bool
}

struct FeeEstimateResponse: Codable {
    let feePerByte: Int64

    enum CodingKeys: String, CodingKey {
        case feePerByte = "fee_per_byte"
    }
}

// MARK: - Errors

/// Errors thrown by wallet operations.
public enum WalletError: Error, LocalizedError {
    case invalidResponse
    case httpError(statusCode: Int, message: String)
    case networkError(Error)
    case encodingError(Error)
    case decodingError(Error)

    public var errorDescription: String? {
        switch self {
        case .invalidResponse:
            return "Invalid response from server"
        case .httpError(let code, let message):
            return "HTTP error \(code): \(message)"
        case .networkError(let error):
            return "Network error: \(error.localizedDescription)"
        case .encodingError(let error):
            return "Encoding error: \(error.localizedDescription)"
        case .decodingError(let error):
            return "Decoding error: \(error.localizedDescription)"
        }
    }
}
