import Foundation

// MARK: - Configuration

/// Configuration for the Synor Database client.
public struct DatabaseConfig {
    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://db.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: - Key-Value Store Types

/// A key-value pair with metadata.
public struct KeyValue: Codable, Sendable {
    public let key: String
    public let value: AnyCodable
    public let ttl: Int?
    public let createdAt: Int64?
    public let updatedAt: Int64?
}

/// Response for key-value get operations.
public struct KVGetResponse: Codable {
    public let value: AnyCodable?
}

/// Response for key-value list operations.
public struct KVListResponse: Codable {
    public let items: [KeyValue]?
}

// MARK: - Document Store Types

/// A document with metadata.
public struct Document: Codable, Sendable {
    public let id: String
    public let collection: String
    public let data: [String: AnyCodable]
    public let createdAt: Int64
    public let updatedAt: Int64
}

/// Response for document creation.
public struct CreateDocumentResponse: Codable {
    public let id: String
}

/// Response for document queries.
public struct DocumentListResponse: Codable {
    public let documents: [Document]?
}

/// Query parameters for document store.
public struct DocumentQuery: Codable {
    public let filter: [String: AnyCodable]?
    public let sort: [String: Int]?
    public let limit: Int?
    public let offset: Int?
    public let projection: [String]?

    public init(
        filter: [String: AnyCodable]? = nil,
        sort: [String: Int]? = nil,
        limit: Int? = nil,
        offset: Int? = nil,
        projection: [String]? = nil
    ) {
        self.filter = filter
        self.sort = sort
        self.limit = limit
        self.offset = offset
        self.projection = projection
    }
}

// MARK: - Vector Store Types

/// A vector entry with metadata.
public struct VectorEntry: Codable, Sendable {
    public let id: String
    public let vector: [Double]
    public let metadata: [String: AnyCodable]?

    public init(id: String, vector: [Double], metadata: [String: AnyCodable]? = nil) {
        self.id = id
        self.vector = vector
        self.metadata = metadata
    }
}

/// Result from a vector search.
public struct SearchResult: Codable, Sendable {
    public let id: String
    public let score: Double
    public let vector: [Double]?
    public let metadata: [String: AnyCodable]?
}

/// Response for vector search operations.
public struct SearchListResponse: Codable {
    public let results: [SearchResult]?
}

// MARK: - Time Series Types

/// A time series data point.
public struct DataPoint: Codable, Sendable {
    public let timestamp: Int64
    public let value: Double
    public let tags: [String: String]?

    public init(timestamp: Int64, value: Double, tags: [String: String]? = nil) {
        self.timestamp = timestamp
        self.value = value
        self.tags = tags
    }
}

/// Time range for queries.
public struct TimeRange: Codable, Sendable {
    public let start: Int64
    public let end: Int64

    public init(start: Int64, end: Int64) {
        self.start = start
        self.end = end
    }
}

/// Aggregation settings for time series queries.
public struct Aggregation: Codable, Sendable {
    public let function: String
    public let interval: String

    public init(function: String, interval: String) {
        self.function = function
        self.interval = interval
    }
}

/// Response for time series queries.
public struct DataPointListResponse: Codable {
    public let points: [DataPoint]?
}

// MARK: - Error Types

/// Errors that can occur during database operations.
public enum DatabaseError: Error, LocalizedError {
    case clientClosed
    case httpError(statusCode: Int, message: String, code: String?)
    case networkError(Error)
    case encodingError(Error)
    case decodingError(Error)
    case invalidResponse

    public var errorDescription: String? {
        switch self {
        case .clientClosed:
            return "Client has been closed"
        case .httpError(let statusCode, let message, _):
            return "HTTP \(statusCode): \(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)"
        case .invalidResponse:
            return "Invalid response"
        }
    }
}

// MARK: - Helper Types

/// Type-erased codable value for dynamic JSON.
public struct AnyCodable: Codable, Sendable {
    public let value: Any

    public init(_ value: Any) {
        self.value = value
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()

        if container.decodeNil() {
            value = NSNull()
        } else if let bool = try? container.decode(Bool.self) {
            value = bool
        } else if let int = try? container.decode(Int.self) {
            value = int
        } else if let double = try? container.decode(Double.self) {
            value = double
        } else if let string = try? container.decode(String.self) {
            value = string
        } else if let array = try? container.decode([AnyCodable].self) {
            value = array.map { $0.value }
        } else if let dict = try? container.decode([String: AnyCodable].self) {
            value = dict.mapValues { $0.value }
        } else {
            throw DecodingError.dataCorruptedError(in: container, debugDescription: "Unable to decode value")
        }
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()

        switch value {
        case is NSNull:
            try container.encodeNil()
        case let bool as Bool:
            try container.encode(bool)
        case let int as Int:
            try container.encode(int)
        case let double as Double:
            try container.encode(double)
        case let string as String:
            try container.encode(string)
        case let array as [Any]:
            try container.encode(array.map { AnyCodable($0) })
        case let dict as [String: Any]:
            try container.encode(dict.mapValues { AnyCodable($0) })
        default:
            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: encoder.codingPath, debugDescription: "Unable to encode value"))
        }
    }
}

/// Health check response.
public struct HealthResponse: Codable {
    public let status: String
}
