import { exec } from "child_process";
import path from "path";
import fs from "fs";
import archiver from "archiver";
import Database from "better-sqlite3";
import { generateAdConfig, createAssetsSymlink, cleanupBuildArtifacts } from "./configGenerator";
import { ensureDir } from "./storageService";

const TEMPLATE_DIR = path.resolve(__dirname, "../../../../templates/coloring");
const BUILD_TIMEOUT_MS = 120_000; // 单次构建超时 120s

export class BuildService {
  private queue: Array<() => Promise<void>> = [];
  private running = false;

  constructor(private db: Database.Database, private storageDir: string) {}

  enqueue(
    buildId: string,
    creativeId: string,
    platforms: string[],
    theme: Record<string, string>
  ): void {
    this.queue.push(() => this.build(buildId, creativeId, platforms, theme));
    if (!this.running) {
      this.processQueue();
    }
  }

  private async processQueue(): Promise<void> {
    this.running = true;
    while (this.queue.length > 0) {
      const task = this.queue.shift()!;
      try {
        await task();
      } catch (err) {
        console.error("[build] Queue task failed:", err);
      }
    }
    this.running = false;
  }

  private async build(
    buildId: string,
    creativeId: string,
    platforms: string[],
    theme: Record<string, string>
  ): Promise<void> {
    const startTime = new Date().toISOString();

    try {
      // 更新状态 → building
      this.db
        .prepare("UPDATE builds SET status = 'building', started_at = ? WHERE id = ?")
        .run(startTime, buildId);

      // 1. 创建 symlink
      createAssetsSymlink(creativeId, this.storageDir);

      // 2. 生成 _ad_config_.ts
      const configContent = generateAdConfig({
        creativeId,
        theme,
        storageDir: this.storageDir,
      });
      const configPath = path.join(TEMPLATE_DIR, "src", "filler", "_ad_config_.ts");
      fs.writeFileSync(configPath, configContent, "utf-8");
      console.log(`[build] Generated _ad_config_.ts for creative ${creativeId}`);

      // 3. 构建输出目录
      const buildOutputDir = path.join(
        this.storageDir,
        "creatives",
        creativeId,
        "builds",
        buildId
      );
      ensureDir(buildOutputDir);

      // 4. 逐平台构建
      const results: Array<{ platform: string; fileSize: number }> = [];

      for (const platform of platforms) {
        console.log(`[build] Building ${platform} for creative ${creativeId}...`);
        await this.runViteBuild(platform);
        await this.collectOutput(buildOutputDir, platform, results);
      }

      // 5. 复制预览产物（用第一个平台的输出即可，供真机扫码测试）
      const previewSrc = path.join(TEMPLATE_DIR, "dist", platforms[0], "index.html");
      const previewDir = path.join(this.storageDir, "previews");
      ensureDir(previewDir);
      const previewId = buildId.replace(/-/g, "").slice(0, 8);
      const previewPath = path.join(previewDir, `${previewId}.html`);
      fs.copyFileSync(previewSrc, previewPath);
      console.log(`[build] Preview file: ${previewPath} (from ${platforms[0]})`);

      // 7. 打包 ZIP
      await this.createZip(buildOutputDir, results);

      // 6. 更新数据库
      const finishedAt = new Date().toISOString();
      this.db
        .prepare(
          `UPDATE builds SET status = 'completed', results = ?, finished_at = ? WHERE id = ?`
        )
        .run(JSON.stringify(results), finishedAt, buildId);

      // 更新创意状态
      this.db
        .prepare(
          "UPDATE creatives SET status = 'built', updated_at = datetime('now') WHERE id = ?"
        )
        .run(creativeId);

      console.log(`[build] Build ${buildId} completed: ${results.map((r) => r.platform).join(", ")}`);
    } catch (err: any) {
      console.error(`[build] Build ${buildId} failed:`, err.message);
      this.db
        .prepare("UPDATE builds SET status = 'failed', error_log = ? WHERE id = ?")
        .run(err.message || "Unknown error", buildId);
      this.db
        .prepare(
          "UPDATE creatives SET status = 'assets_ready', updated_at = datetime('now') WHERE id = ?"
        )
        .run(creativeId);
    } finally {
      // 清理临时文件
      cleanupBuildArtifacts();
    }
  }

  private runViteBuild(platform: string): Promise<void> {
    return new Promise((resolve, reject) => {
      const cmd = `cd ${TEMPLATE_DIR} && AD_CONFIG_PATH=src/filler/_ad_config_.ts npx vite build --mode ${platform}`;
      console.log(`[build] Executing: ${cmd}`);

      exec(cmd, { timeout: BUILD_TIMEOUT_MS }, (error, stdout, stderr) => {
        if (stdout) console.log(`[vite:${platform}]`, stdout.slice(-500));
        if (stderr && !stderr.includes("vite")) console.error(`[vite:${platform}]`, stderr.slice(-500));

        if (error) {
          reject(new Error(`Vite build failed for ${platform}: ${error.message}`));
        } else {
          resolve();
        }
      });
    });
  }

  private async collectOutput(
    buildOutputDir: string,
    platform: string,
    results: Array<{ platform: string; fileSize: number }>
  ): Promise<void> {
    const distPath = path.join(TEMPLATE_DIR, "dist", platform, "index.html");
    const destDir = path.join(buildOutputDir, platform);
    ensureDir(destDir);
    const destPath = path.join(destDir, "index.html");

    if (!fs.existsSync(distPath)) {
      throw new Error(`Build output not found for platform ${platform}: ${distPath}`);
    }

    fs.copyFileSync(distPath, destPath);
    const stat = fs.statSync(destPath);
    results.push({ platform, fileSize: stat.size });
  }

  private createZip(
    buildOutputDir: string,
    results: Array<{ platform: string; fileSize: number }>
  ): Promise<void> {
    return new Promise((resolve, reject) => {
      const zipPath = path.join(buildOutputDir, "all.zip");
      const output = fs.createWriteStream(zipPath);
      const archive = archiver("zip", { zlib: { level: 9 } });

      output.on("close", resolve);
      archive.on("error", reject);

      archive.pipe(output);

      for (const r of results) {
        const filePath = path.join(buildOutputDir, r.platform, "index.html");
        if (fs.existsSync(filePath)) {
          archive.file(filePath, { name: `${r.platform}/index.html` });
        }
      }

      archive.finalize();
    });
  }
}
