import { z } from "zod";
import { apiClient, stripSensitiveData, withProviderErrorHandling, assertValidServerId, uploadSshKeyWithConflict } from "./base.js";
import { withRetry } from "../utils/retry.js";
import { formatSnapshotCost } from "../constants.js";
export const DODropletSchema = z.object({
    droplet: z.object({
        id: z.number(),
        status: z.string(),
        networks: z.object({
            v4: z.array(z.object({
                type: z.string(),
                ip_address: z.string(),
            })).optional(),
        }).optional(),
    }),
});
function extractDOError(data) {
    const d = data;
    return d?.message;
}
export class DigitalOceanProvider {
    name = "digitalocean";
    displayName = "DigitalOcean";
    apiToken;
    baseUrl = "https://api.digitalocean.com/v2";
    constructor(apiToken) {
        this.apiToken = apiToken;
    }
    async validateToken(token) {
        try {
            await withRetry(async () => {
                await apiClient.get(`${this.baseUrl}/account`, {
                    headers: { Authorization: `Bearer ${token}` },
                });
            });
            return true;
        }
        catch {
            return false;
        }
    }
    async uploadSshKey(name, publicKey) {
        return uploadSshKeyWithConflict(name, publicKey, {
            apiToken: this.apiToken,
            baseUrl: this.baseUrl,
            createPath: "/account/keys",
            bodyKeyField: "public_key",
            listPath: "/account/keys?per_page=200",
            listArrayField: "ssh_keys",
            listKeyField: "public_key",
            conflictStatuses: [422],
        });
    }
    async createServer(config) {
        return withProviderErrorHandling("create server", async () => {
            const body = {
                name: config.name,
                size: config.size,
                region: config.region,
                image: "ubuntu-24-04-x64",
                user_data: config.cloudInit,
            };
            if (config.sshKeyIds?.length) {
                body.ssh_keys = config.sshKeyIds.map(Number);
            }
            const response = await apiClient.post(`${this.baseUrl}/droplets`, body, {
                headers: {
                    Authorization: `Bearer ${this.apiToken}`,
                    "Content-Type": "application/json",
                },
            });
            const parsed = DODropletSchema.parse(response.data);
            const droplet = parsed.droplet;
            const ip = droplet.networks?.v4?.find((n) => n.type === "public")?.ip_address ||
                "pending";
            return {
                id: droplet.id.toString(),
                ip,
                status: droplet.status,
            };
        }, extractDOError);
    }
    async getServerDetails(serverId) {
        assertValidServerId(serverId);
        return withProviderErrorHandling("get server details", () => withRetry(async () => {
            const response = await apiClient.get(`${this.baseUrl}/droplets/${serverId}`, {
                headers: { Authorization: `Bearer ${this.apiToken}` },
            });
            const parsed = DODropletSchema.parse(response.data);
            const droplet = parsed.droplet;
            const ip = droplet.networks?.v4?.find((n) => n.type === "public")?.ip_address ||
                "pending";
            return {
                id: droplet.id.toString(),
                ip,
                status: droplet.status === "active" ? "running" : droplet.status,
            };
        }));
    }
    async getServerStatus(serverId) {
        assertValidServerId(serverId);
        return withProviderErrorHandling("get server status", () => withRetry(async () => {
            const response = await apiClient.get(`${this.baseUrl}/droplets/${serverId}`, {
                headers: { Authorization: `Bearer ${this.apiToken}` },
            });
            // Normalize DO status: "active" → "running" (init.ts checks for "running")
            const parsed = DODropletSchema.parse(response.data);
            const doStatus = parsed.droplet.status;
            return doStatus === "active" ? "running" : doStatus;
        }));
    }
    async destroyServer(serverId) {
        assertValidServerId(serverId);
        return withProviderErrorHandling("destroy server", async () => {
            await apiClient.delete(`${this.baseUrl}/droplets/${serverId}`, {
                headers: { Authorization: `Bearer ${this.apiToken}` },
            });
        }, extractDOError);
    }
    async rebootServer(serverId) {
        assertValidServerId(serverId);
        return withProviderErrorHandling("reboot server", async () => {
            await apiClient.post(`${this.baseUrl}/droplets/${serverId}/actions`, { type: "reboot" }, {
                headers: {
                    Authorization: `Bearer ${this.apiToken}`,
                    "Content-Type": "application/json",
                },
            });
        }, extractDOError);
    }
    getRegions() {
        return [
            { id: "nyc1", name: "New York 1", location: "USA" },
            { id: "sfo3", name: "San Francisco 3", location: "USA" },
            { id: "ams3", name: "Amsterdam 3", location: "Netherlands" },
            { id: "sgp1", name: "Singapore 1", location: "Singapore" },
            { id: "lon1", name: "London 1", location: "UK" },
            { id: "fra1", name: "Frankfurt 1", location: "Germany" },
        ];
    }
    getServerSizes() {
        // Minimum 2GB RAM required for Coolify
        return [
            { id: "s-2vcpu-2gb", name: "Basic 2GB", vcpu: 2, ram: 2, disk: 60, price: "$12/mo" },
            { id: "s-2vcpu-4gb", name: "Basic 4GB", vcpu: 2, ram: 4, disk: 80, price: "$24/mo" },
            { id: "s-4vcpu-8gb", name: "General 8GB", vcpu: 4, ram: 8, disk: 160, price: "$48/mo" },
        ];
    }
    async getAvailableLocations() {
        try {
            return await withRetry(async () => {
                const response = await apiClient.get(`${this.baseUrl}/regions`, {
                    headers: { Authorization: `Bearer ${this.apiToken}` },
                });
                return response.data.regions
                    .filter((r) => r.available)
                    .map((r) => ({
                    id: r.slug,
                    name: r.name,
                    location: r.slug,
                }));
            });
        }
        catch (error) {
            stripSensitiveData(error);
            return this.getRegions();
        }
    }
    async getAvailableServerTypes(location, mode) {
        try {
            return await withRetry(async () => {
                const response = await apiClient.get(`${this.baseUrl}/sizes`, {
                    headers: { Authorization: `Bearer ${this.apiToken}` },
                });
                const MIN_RAM_MB = mode === "bare" ? 0 : 2048; // Coolify requires at least 2GB RAM, bare has no minimum
                const MIN_VCPUS = mode === "bare" ? 0 : 2; // Coolify requires at least 2 CPUs
                const sizes = response.data.sizes.filter((s) => s.available &&
                    s.regions.includes(location) &&
                    s.memory >= MIN_RAM_MB &&
                    s.vcpus >= MIN_VCPUS);
                if (sizes.length === 0) {
                    return this.getServerSizes();
                }
                return sizes.map((s) => ({
                    id: s.slug,
                    name: s.slug.toUpperCase(),
                    vcpu: s.vcpus,
                    ram: s.memory / 1024,
                    disk: s.disk,
                    price: `$${s.price_monthly.toFixed(2)}/mo`,
                }));
            });
        }
        catch (error) {
            stripSensitiveData(error);
            return this.getServerSizes();
        }
    }
    async createSnapshot(serverId, name) {
        assertValidServerId(serverId);
        return withProviderErrorHandling("create snapshot", async () => {
            await apiClient.post(`${this.baseUrl}/droplets/${serverId}/actions`, { type: "snapshot", name }, {
                headers: {
                    Authorization: `Bearer ${this.apiToken}`,
                    "Content-Type": "application/json",
                },
            });
            // DO snapshot creation is async; return a pending snapshot info
            return {
                id: "pending",
                serverId,
                name,
                status: "pending",
                sizeGb: 0,
                createdAt: new Date().toISOString(),
                costPerMonth: "pending",
            };
        }, extractDOError);
    }
    async listSnapshots(serverId) {
        assertValidServerId(serverId);
        return withProviderErrorHandling("list snapshots", () => withRetry(async () => {
            const response = await apiClient.get(`${this.baseUrl}/droplets/${serverId}/snapshots?per_page=100`, { headers: { Authorization: `Bearer ${this.apiToken}` } });
            return response.data.snapshots.map((snap) => ({
                id: snap.id.toString(),
                serverId,
                name: snap.name,
                status: "available",
                sizeGb: snap.size_gigabytes,
                createdAt: snap.created_at,
                costPerMonth: formatSnapshotCost("digitalocean", snap.size_gigabytes),
            }));
        }), extractDOError);
    }
    async deleteSnapshot(snapshotId) {
        assertValidServerId(snapshotId);
        return withProviderErrorHandling("delete snapshot", async () => {
            await apiClient.delete(`${this.baseUrl}/snapshots/${snapshotId}`, {
                headers: { Authorization: `Bearer ${this.apiToken}` },
            });
        }, extractDOError);
    }
    async restoreSnapshot(serverId, snapshotId) {
        assertValidServerId(serverId);
        assertValidServerId(snapshotId);
        return withProviderErrorHandling("restore snapshot", async () => {
            await apiClient.post(`${this.baseUrl}/droplets/${serverId}/actions`, { type: "restore", image: Number(snapshotId) }, { headers: { Authorization: `Bearer ${this.apiToken}`, "Content-Type": "application/json" } });
        }, extractDOError);
    }
    async getSnapshotCostEstimate(serverId) {
        assertValidServerId(serverId);
        return withProviderErrorHandling("get snapshot cost", () => withRetry(async () => {
            const response = await apiClient.get(`${this.baseUrl}/droplets/${serverId}`, {
                headers: { Authorization: `Bearer ${this.apiToken}` },
            });
            const diskGb = response.data.droplet.disk;
            return formatSnapshotCost("digitalocean", diskGb);
        }), extractDOError);
    }
    async findServerByIp(ip) {
        return withProviderErrorHandling("find server by IP", async () => {
            const response = await apiClient.get(`${this.baseUrl}/droplets?per_page=100`, {
                headers: { Authorization: `Bearer ${this.apiToken}` },
            });
            const droplets = response.data.droplets;
            const found = droplets.find((droplet) => droplet.networks?.v4?.find((n) => n.type === "public")?.ip_address === ip);
            return found ? found.id.toString() : null;
        }, extractDOError);
    }
}
//# sourceMappingURL=digitalocean.js.map