import fs from "node:fs/promises";
import path from "node:path";
import COS from "cos-nodejs-sdk-v5";
import { config } from "./config.js";
import { ensureDir, removeIfExists, safeStoragePath } from "./utils.js";

export type StorageProvider = "local" | "cos";

export type StoredObject = {
  key: string;
  provider: StorageProvider;
};

type StorageService = {
  provider: StorageProvider;
  putObject(key: string, buffer: Buffer): Promise<StoredObject>;
  deleteObject(key: string): Promise<void>;
  publicUrl(key: string): string;
};

function normalizeKey(key: string) {
  return key.replace(/\\/g, "/").replace(/^\/+/, "");
}

function joinUrl(base: string, key: string) {
  return `${base.replace(/\/+$/g, "")}/${normalizeKey(key).split("/").map(encodeURIComponent).join("/")}`;
}

class LocalStorageService implements StorageService {
  provider: StorageProvider = "local";

  async putObject(key: string, buffer: Buffer) {
    const normalized = normalizeKey(key);
    const fullPath = safeStoragePath(normalized);
    await ensureDir(path.dirname(fullPath));
    await fs.writeFile(fullPath, buffer);
    return { key: normalized, provider: this.provider };
  }

  async deleteObject(key: string) {
    await removeIfExists(safeStoragePath(normalizeKey(key)));
  }

  publicUrl(key: string) {
    const normalized = normalizeKey(key);
    if (config.publicBaseUrl) {
      return joinUrl(`${config.publicBaseUrl}/storage`, normalized);
    }
    return `/storage/${normalized}`;
  }
}

class CosStorageService implements StorageService {
  provider: StorageProvider = "cos";
  private cos: COS;

  constructor() {
    const missing = [
      ["COS_SECRET_ID", config.cos.secretId],
      ["COS_SECRET_KEY", config.cos.secretKey],
      ["COS_BUCKET", config.cos.bucket],
      ["COS_REGION", config.cos.region],
      ["CDN_BASE_URL", config.cdnBaseUrl]
    ].filter(([, value]) => !value).map(([key]) => key);
    if (missing.length > 0) {
      throw new Error(`COS 存储配置缺失：${missing.join(", ")}`);
    }
    this.cos = new COS({
      SecretId: config.cos.secretId,
      SecretKey: config.cos.secretKey
    });
  }

  async putObject(key: string, buffer: Buffer) {
    const objectKey = normalizeKey(config.cos.prefix ? `${config.cos.prefix}/${key}` : key);
    await new Promise<void>((resolve, reject) => {
      this.cos.putObject({
        Bucket: config.cos.bucket,
        Region: config.cos.region,
        Key: objectKey,
        Body: buffer
      }, (error) => {
        if (error) reject(error);
        else resolve();
      });
    });
    return { key: objectKey, provider: this.provider };
  }

  async deleteObject(key: string) {
    await new Promise<void>((resolve, reject) => {
      this.cos.deleteObject({
        Bucket: config.cos.bucket,
        Region: config.cos.region,
        Key: normalizeKey(key)
      }, (error) => {
        if (error) reject(error);
        else resolve();
      });
    });
  }

  publicUrl(key: string) {
    return joinUrl(config.cdnBaseUrl, key);
  }
}

export const storage = config.storageDriver === "cos"
  ? new CosStorageService()
  : new LocalStorageService();

const localStorage = storage.provider === "local" ? storage : new LocalStorageService();

export function publicObjectUrl(key: string, provider: StorageProvider) {
  if (provider === "cos") {
    if (!config.cdnBaseUrl) return normalizeKey(key);
    return joinUrl(config.cdnBaseUrl, key);
  }
  return localStorage.publicUrl(key);
}

export async function deleteStoredObject(key: string, provider: StorageProvider) {
  if (provider === storage.provider) {
    await storage.deleteObject(key);
    return;
  }
  if (provider === "local") {
    await localStorage.deleteObject(key);
  }
}

export function storageStatus() {
  return {
    driver: storage.provider,
    cdnEnabled: Boolean(config.cdnBaseUrl),
    cosConfigured: Boolean(config.cos.bucket && config.cos.region && config.cos.secretId && config.cos.secretKey)
  };
}
