package io.synor.storage

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

/**
 * Status of a pinned item.
 */
@Serializable
enum class PinStatus {
    @SerialName("queued") QUEUED,
    @SerialName("pinning") PINNING,
    @SerialName("pinned") PINNED,
    @SerialName("failed") FAILED,
    @SerialName("unpinned") UNPINNED
}

/**
 * Hash algorithm for content addressing.
 */
@Serializable
enum class HashAlgorithm {
    @SerialName("sha2-256") SHA2_256,
    @SerialName("blake3") BLAKE3
}

/**
 * Type of directory entry.
 */
@Serializable
enum class EntryType {
    @SerialName("file") FILE,
    @SerialName("directory") DIRECTORY
}

/**
 * Configuration for the storage client.
 */
data class StorageConfig(
    val apiKey: String,
    val endpoint: String = "https://storage.synor.io/v1",
    val gateway: String = "https://gateway.synor.io",
    val timeout: Long = 60_000L,
    val retries: Int = 3,
    val debug: Boolean = false
) {
    class Builder(private val apiKey: String) {
        private var endpoint: String = "https://storage.synor.io/v1"
        private var gateway: String = "https://gateway.synor.io"
        private var timeout: Long = 60_000L
        private var retries: Int = 3
        private var debug: Boolean = false

        fun endpoint(endpoint: String) = apply { this.endpoint = endpoint }
        fun gateway(gateway: String) = apply { this.gateway = gateway }
        fun timeout(timeout: Long) = apply { this.timeout = timeout }
        fun retries(retries: Int) = apply { this.retries = retries }
        fun debug(debug: Boolean) = apply { this.debug = debug }

        fun build() = StorageConfig(apiKey, endpoint, gateway, timeout, retries, debug)
    }

    companion object {
        fun builder(apiKey: String) = Builder(apiKey)
    }
}

/**
 * Options for file upload.
 */
@Serializable
data class UploadOptions(
    val name: String? = null,
    @SerialName("wrap_with_directory") val wrapWithDirectory: Boolean = false,
    @SerialName("hash_algorithm") val hashAlgorithm: HashAlgorithm = HashAlgorithm.SHA2_256,
    @SerialName("chunk_size") val chunkSize: Int? = null,
    @SerialName("pin_duration_days") val pinDurationDays: Int? = null
)

/**
 * Response from upload operation.
 */
@Serializable
data class UploadResponse(
    val cid: String,
    val size: Long,
    val name: String? = null,
    @SerialName("created_at") val createdAt: String
)

/**
 * Pin information.
 */
@Serializable
data class Pin(
    val cid: String,
    val name: String? = null,
    val status: PinStatus,
    val size: Long,
    @SerialName("created_at") val createdAt: String,
    @SerialName("expires_at") val expiresAt: String? = null,
    val delegates: List<String>? = null
)

/**
 * Request to pin content.
 */
@Serializable
data class PinRequest(
    val cid: String,
    val name: String? = null,
    @SerialName("duration_days") val durationDays: Int? = null,
    val origins: List<String>? = null,
    val meta: Map<String, String>? = null
)

/**
 * CAR (Content Addressable Archive) file.
 */
@Serializable
data class CarFile(
    val cid: String,
    val size: Long,
    val roots: List<String>,
    @SerialName("created_at") val createdAt: String
)

/**
 * Entry in a CAR file for creation.
 */
data class CarEntry(
    val path: String,
    val content: ByteArray
) {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is CarEntry) return false
        return path == other.path && content.contentEquals(other.content)
    }

    override fun hashCode(): Int {
        return 31 * path.hashCode() + content.contentHashCode()
    }
}

/**
 * Directory entry.
 */
@Serializable
data class DirectoryEntry(
    val name: String,
    val cid: String,
    val size: Long,
    val type: EntryType
)

/**
 * File entry for directory creation.
 */
data class FileEntry(
    val path: String,
    val content: ByteArray
) {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is FileEntry) return false
        return path == other.path && content.contentEquals(other.content)
    }

    override fun hashCode(): Int {
        return 31 * path.hashCode() + content.contentHashCode()
    }
}

/**
 * Storage usage statistics.
 */
@Serializable
data class StorageStats(
    @SerialName("total_size") val totalSize: Long,
    @SerialName("pin_count") val pinCount: Int,
    @SerialName("bandwidth_used") val bandwidthUsed: Long,
    @SerialName("quota_used") val quotaUsed: Double
)

/**
 * Exception thrown by storage operations.
 */
class StorageException(
    message: String,
    val code: String? = null,
    val statusCode: Int? = null,
    cause: Throwable? = null
) : Exception(message, cause)
