package io.synor.storage;

import java.util.List;

/** Pin status. */
public enum PinStatus {
    QUEUED("queued"), PINNING("pinning"), PINNED("pinned"), FAILED("failed"), UNPINNED("unpinned");
    private final String value;
    PinStatus(String value) { this.value = value; }
    public String getValue() { return value; }
}

/** Hash algorithm. */
public enum HashAlgorithm {
    SHA2_256("sha2-256"), BLAKE3("blake3");
    private final String value;
    HashAlgorithm(String value) { this.value = value; }
    public String getValue() { return value; }
}

/** Entry type. */
public enum EntryType {
    FILE("file"), DIRECTORY("directory");
    private final String value;
    EntryType(String value) { this.value = value; }
    public String getValue() { return value; }
}

/** Storage configuration. */
class StorageConfig {
    private final String apiKey;
    private final String endpoint;
    private final String gateway;
    private final long timeoutSeconds;
    private final boolean debug;

    private StorageConfig(Builder builder) {
        this.apiKey = builder.apiKey;
        this.endpoint = builder.endpoint;
        this.gateway = builder.gateway;
        this.timeoutSeconds = builder.timeoutSeconds;
        this.debug = builder.debug;
    }

    public String getApiKey() { return apiKey; }
    public String getEndpoint() { return endpoint; }
    public String getGateway() { return gateway; }
    public long getTimeoutSeconds() { return timeoutSeconds; }
    public boolean isDebug() { return debug; }

    public static Builder builder() { return new Builder(); }

    public static class Builder {
        private String apiKey;
        private String endpoint = "https://storage.synor.cc/api/v1";
        private String gateway = "https://gateway.synor.cc";
        private long timeoutSeconds = 30;
        private boolean debug = false;

        public Builder apiKey(String apiKey) { this.apiKey = apiKey; return this; }
        public Builder endpoint(String endpoint) { this.endpoint = endpoint; return this; }
        public Builder gateway(String gateway) { this.gateway = gateway; return this; }
        public Builder timeoutSeconds(long seconds) { this.timeoutSeconds = seconds; return this; }
        public Builder debug(boolean debug) { this.debug = debug; return this; }
        public StorageConfig build() { return new StorageConfig(this); }
    }
}

/** Upload options. */
class UploadOptions {
    private boolean pin = true;
    private boolean wrapWithDirectory = false;
    private int cidVersion = 1;
    private HashAlgorithm hashAlgorithm = HashAlgorithm.SHA2_256;

    public static UploadOptions defaults() { return new UploadOptions(); }

    public boolean isPin() { return pin; }
    public boolean isWrapWithDirectory() { return wrapWithDirectory; }
    public int getCidVersion() { return cidVersion; }
    public HashAlgorithm getHashAlgorithm() { return hashAlgorithm; }

    public UploadOptions pin(boolean pin) { this.pin = pin; return this; }
    public UploadOptions wrapWithDirectory(boolean wrap) { this.wrapWithDirectory = wrap; return this; }
    public UploadOptions cidVersion(int version) { this.cidVersion = version; return this; }
    public UploadOptions hashAlgorithm(HashAlgorithm algo) { this.hashAlgorithm = algo; return this; }
}

/** Download options. */
class DownloadOptions {
    private Long offset;
    private Long length;

    public static DownloadOptions defaults() { return new DownloadOptions(); }

    public Long getOffset() { return offset; }
    public Long getLength() { return length; }

    public DownloadOptions offset(long offset) { this.offset = offset; return this; }
    public DownloadOptions length(long length) { this.length = length; return this; }
}

/** Upload response. */
class UploadResponse {
    private String cid;
    private long size;
    private String name;
    private String hash;

    public String getCid() { return cid; }
    public long getSize() { return size; }
    public String getName() { return name; }
    public String getHash() { return hash; }
}

/** Pin. */
class Pin {
    private String cid;
    private PinStatus status;
    private String name;
    private Long size;
    private Long createdAt;
    private Long expiresAt;
    private List<String> delegates;

    public String getCid() { return cid; }
    public PinStatus getStatus() { return status; }
    public String getName() { return name; }
    public Long getSize() { return size; }
    public Long getCreatedAt() { return createdAt; }
    public Long getExpiresAt() { return expiresAt; }
    public List<String> getDelegates() { return delegates; }
}

/** Pin request. */
class PinRequest {
    private String cid;
    private String name;
    private Long duration;
    private List<String> origins;

    public PinRequest(String cid) { this.cid = cid; }

    public PinRequest name(String name) { this.name = name; return this; }
    public PinRequest duration(long duration) { this.duration = duration; return this; }
    public PinRequest origins(List<String> origins) { this.origins = origins; return this; }
}

/** List pins options. */
class ListPinsOptions {
    private Integer limit;
    private Integer offset;
    private List<PinStatus> status;
    private String name;

    public Integer getLimit() { return limit; }
    public Integer getOffset() { return offset; }

    public ListPinsOptions limit(int limit) { this.limit = limit; return this; }
    public ListPinsOptions offset(int offset) { this.offset = offset; return this; }
}

/** List pins response. */
class ListPinsResponse {
    private List<Pin> pins;
    private int total;
    private boolean hasMore;

    public List<Pin> getPins() { return pins; }
    public int getTotal() { return total; }
    public boolean isHasMore() { return hasMore; }
}

/** Gateway URL. */
class GatewayUrl {
    private final String url;
    private final String cid;
    private final String path;

    public GatewayUrl(String url, String cid, String path) {
        this.url = url;
        this.cid = cid;
        this.path = path;
    }

    public String getUrl() { return url; }
    public String getCid() { return cid; }
    public String getPath() { return path; }
}

/** File entry. */
class FileEntry {
    private String name;
    private String content;
    private String cid;

    public FileEntry(String name) { this.name = name; }

    public FileEntry content(byte[] content) {
        this.content = java.util.Base64.getEncoder().encodeToString(content);
        return this;
    }
    public FileEntry cid(String cid) { this.cid = cid; return this; }
}

/** Directory entry. */
class DirectoryEntry {
    private String name;
    private String cid;
    private Long size;
    private EntryType type;

    public String getName() { return name; }
    public String getCid() { return cid; }
    public Long getSize() { return size; }
    public EntryType getType() { return type; }
}

/** CAR block. */
class CarBlock {
    private String cid;
    private String data;
    private Long size;

    public String getCid() { return cid; }
    public String getData() { return data; }
    public Long getSize() { return size; }
}

/** CAR file. */
class CarFile {
    private int version;
    private List<String> roots;
    private List<CarBlock> blocks;
    private Long size;

    public int getVersion() { return version; }
    public List<String> getRoots() { return roots; }
    public List<CarBlock> getBlocks() { return blocks; }
    public Long getSize() { return size; }
}

/** Import CAR response. */
class ImportCarResponse {
    private List<String> roots;
    private int blocksImported;

    public List<String> getRoots() { return roots; }
    public int getBlocksImported() { return blocksImported; }
}

/** Storage stats. */
class StorageStats {
    private long totalSize;
    private int pinCount;
    private BandwidthStats bandwidth;

    public long getTotalSize() { return totalSize; }
    public int getPinCount() { return pinCount; }
    public BandwidthStats getBandwidth() { return bandwidth; }
}

/** Bandwidth stats. */
class BandwidthStats {
    private long upload;
    private long download;

    public long getUpload() { return upload; }
    public long getDownload() { return download; }
}

/** Storage exception. */
class StorageException extends RuntimeException {
    private final int statusCode;
    private final String code;

    public StorageException(String message, int statusCode, String code) {
        super(message);
        this.statusCode = statusCode;
        this.code = code;
    }

    public int getStatusCode() { return statusCode; }
    public String getCode() { return code; }
}
