import Foundation

// MARK: - Configuration

/// Configuration for the Synor Mining client.
public struct MiningConfig {
    public let apiKey: String
    public let endpoint: String
    public let timeout: TimeInterval
    public let retries: Int
    public let debug: Bool

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

// MARK: - Enums

/// Device type.
public enum DeviceType: String, Codable, Sendable {
    case cpu
    case gpu_nvidia
    case gpu_amd
    case asic
}

/// Device status.
public enum DeviceStatus: String, Codable, Sendable {
    case idle
    case mining
    case error
    case offline
}

/// Connection status.
public enum ConnectionStatus: String, Codable, Sendable {
    case disconnected
    case connecting
    case connected
    case reconnecting
}

/// Time period.
public enum TimePeriod: String, Codable, Sendable {
    case hour
    case day
    case week
    case month
    case all
}

/// Submit result status.
public enum SubmitResultStatus: String, Codable, Sendable {
    case accepted
    case rejected
    case stale
}

// MARK: - Pool Types

/// Pool configuration.
public struct PoolConfig {
    public var url: String
    public var user: String
    public var password: String?
    public var algorithm: String?
    public var difficulty: Double?

    public init(
        url: String,
        user: String,
        password: String? = nil,
        algorithm: String? = nil,
        difficulty: Double? = nil
    ) {
        self.url = url
        self.user = user
        self.password = password
        self.algorithm = algorithm
        self.difficulty = difficulty
    }
}

/// Stratum connection.
public struct StratumConnection: Codable, Sendable {
    public let id: String
    public let pool: String
    public let status: ConnectionStatus
    public let algorithm: String
    public let difficulty: Double
    public let connectedAt: Int64
    public let acceptedShares: Int
    public let rejectedShares: Int
    public let staleShares: Int
    public let lastShareAt: Int64?
}

/// Pool stats.
public struct PoolStats: Codable, Sendable {
    public let url: String
    public let workers: Int
    public let hashrate: Double
    public let difficulty: Double
    public let lastBlock: Int64
    public let blocksFound24h: Int
    public let luck: Double
}

// MARK: - Mining Types

/// Template transaction.
public struct TemplateTransaction: Codable, Sendable {
    public let txid: String
    public let data: String
    public let fee: String
    public let weight: Int
}

/// Block template.
public struct BlockTemplate: Codable, Sendable {
    public let id: String
    public let previousBlockHash: String
    public let merkleRoot: String
    public let timestamp: Int64
    public let bits: String
    public let height: Int64
    public let coinbaseValue: String
    public let transactions: [TemplateTransaction]
    public let target: String
    public let algorithm: String
    public let extraNonce: String
}

/// Mined work.
public struct MinedWork {
    public var templateId: String
    public var nonce: String
    public var extraNonce: String
    public var timestamp: Int64
    public var hash: String

    public init(
        templateId: String,
        nonce: String,
        extraNonce: String,
        timestamp: Int64,
        hash: String
    ) {
        self.templateId = templateId
        self.nonce = nonce
        self.extraNonce = extraNonce
        self.timestamp = timestamp
        self.hash = hash
    }
}

/// Share info.
public struct ShareInfo: Codable, Sendable {
    public let hash: String
    public let difficulty: Double
    public let timestamp: Int64
    public let accepted: Bool
}

/// Submit result.
public struct SubmitResult: Codable, Sendable {
    public let status: SubmitResultStatus
    public let share: ShareInfo
    public let blockFound: Bool
    public let reason: String?
    public let blockHash: String?
    public let reward: String?
}

// MARK: - Stats Types

/// Hashrate.
public struct Hashrate: Codable, Sendable {
    public let current: Double
    public let average1h: Double
    public let average24h: Double
    public let peak: Double
    public let unit: String
}

/// Share stats.
public struct ShareStats: Codable, Sendable {
    public let accepted: Int
    public let rejected: Int
    public let stale: Int
    public let total: Int
    public let acceptRate: Double
}

/// Device temperature.
public struct DeviceTemperature: Codable, Sendable {
    public let current: Double
    public let max: Double
    public let throttling: Bool
}

/// Earnings snapshot.
public struct EarningsSnapshot: Codable, Sendable {
    public let today: String
    public let yesterday: String
    public let thisWeek: String
    public let thisMonth: String
    public let total: String
    public let currency: String
}

/// Mining stats.
public struct MiningStats: Codable, Sendable {
    public let hashrate: Hashrate
    public let shares: ShareStats
    public let uptime: Int64
    public let efficiency: Double
    public let earnings: EarningsSnapshot
    public let powerConsumption: Double?
    public let temperature: DeviceTemperature?
}

/// Earnings breakdown.
public struct EarningsBreakdown: Codable, Sendable {
    public let date: Int64
    public let amount: String
    public let blocks: Int
    public let shares: Int
    public let hashrate: Double
}

/// Earnings.
public struct Earnings: Codable, Sendable {
    public let period: TimePeriod
    public let startDate: Int64
    public let endDate: Int64
    public let amount: String
    public let blocks: Int
    public let shares: Int
    public let averageHashrate: Double
    public let currency: String
    public let breakdown: [EarningsBreakdown]
}

// MARK: - Device Types

/// Mining device.
public struct MiningDevice: Codable, Sendable {
    public let id: String
    public let name: String
    public let type: DeviceType
    public let status: DeviceStatus
    public let hashrate: Double
    public let temperature: Double
    public let fanSpeed: Double
    public let powerDraw: Double
    public let memoryUsed: Int64
    public let memoryTotal: Int64
    public let driver: String?
    public let firmware: String?
}

/// Device configuration.
public struct DeviceConfig {
    public var enabled: Bool
    public var intensity: Int?
    public var powerLimit: Int?
    public var coreClockOffset: Int?
    public var memoryClockOffset: Int?
    public var fanSpeed: Int?

    public init(
        enabled: Bool,
        intensity: Int? = nil,
        powerLimit: Int? = nil,
        coreClockOffset: Int? = nil,
        memoryClockOffset: Int? = nil,
        fanSpeed: Int? = nil
    ) {
        self.enabled = enabled
        self.intensity = intensity
        self.powerLimit = powerLimit
        self.coreClockOffset = coreClockOffset
        self.memoryClockOffset = memoryClockOffset
        self.fanSpeed = fanSpeed
    }
}

/// Worker info.
public struct WorkerInfo: Codable, Sendable {
    public let id: String
    public let name: String
    public let status: ConnectionStatus
    public let hashrate: Hashrate
    public let shares: ShareStats
    public let devices: [MiningDevice]
    public let lastSeen: Int64
    public let uptime: Int64
}

/// Mining algorithm.
public struct MiningAlgorithm: Codable, Sendable {
    public let name: String
    public let displayName: String
    public let hashUnit: String
    public let profitability: String
    public let difficulty: Double
    public let blockReward: String
    public let blockTime: Int
}

// MARK: - Response Types

struct DevicesResponse: Codable {
    let devices: [MiningDevice]?
}

struct WorkersResponse: Codable {
    let workers: [WorkerInfo]?
}

struct AlgorithmsResponse: Codable {
    let algorithms: [MiningAlgorithm]?
}

// MARK: - Error

/// Error thrown by Mining operations.
public struct MiningError: Error, LocalizedError {
    public let message: String
    public let code: String?
    public let statusCode: Int

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

    public var errorDescription: String? { message }
}
