#!/usr/bin/env node
/**
 * Design System Server (DSS) - CLI Tool
 *
 * Commands:
 *   dss export [projectId] [--output file.json]  Export project for backup
 *   dss import <file.json>                        Import project from backup
 *   dss backup [--output dir]                     Full backup of all data
 *   dss restore <backup.json>                     Restore from full backup
 *   dss health                                    Check server health
 */

import fs from "fs-extra";
import path from "path";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const CONFIG = {
  version: "0.1.0",
  dataDir: path.resolve(__dirname, "../../../.dss"),
  defaultBackupDir: path.resolve(__dirname, "../../../.dss/backups"),
  serverUrl: process.env.DSS_URL || "http://localhost:3456",
};

// === UTILITIES ===

function timestamp(): string {
  return new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
}

function log(msg: string, type: "info" | "success" | "error" | "warn" = "info"): void {
  const colors = {
    info: "\x1b[36m",
    success: "\x1b[32m",
    error: "\x1b[31m",
    warn: "\x1b[33m",
  };
  const reset = "\x1b[0m";
  const prefix = {
    info: "ℹ",
    success: "✓",
    error: "✗",
    warn: "⚠",
  };
  console.log(`${colors[type]}${prefix[type]}${reset} ${msg}`);
}

function printHelp(): void {
  console.log(`
Design System Server (DSS) CLI v${CONFIG.version}

Usage: dss <command> [options]

Commands:
  export [id]              Export a project (or all if no id given)
    --output, -o <file>    Output file path (default: project-name.dss.json)
    --stdout               Output to stdout instead of file

  import <file>            Import a project from backup file
    --force, -f            Overwrite if project exists

  backup                   Create full backup of all projects
    --output, -o <dir>     Output directory (default: .dss/backups/)

  restore <file>           Restore from a full backup
    --force, -f            Overwrite existing data

  health                   Check server health status
    --json                 Output as JSON

  list                     List all projects

Examples:
  dss export my-project-id -o ./backup.json
  dss import ./backup.json
  dss backup
  dss restore ./dss-backup-2024.json
  dss health
`);
}

// === DATA ACCESS ===

interface Project {
  id: string;
  name: string;
  figmaFileKey?: string;
  status: string;
  tokens: unknown[];
  components: unknown[];
  styles: unknown[];
  createdAt: string;
  updatedAt: string;
}

interface DataFile {
  projects: Project[];
  teams: unknown[];
  activities: unknown[];
}

function getDataPath(): string {
  return path.join(CONFIG.dataDir, "data.json");
}

function loadData(): DataFile {
  const dataPath = getDataPath();
  if (!fs.existsSync(dataPath)) {
    return { projects: [], teams: [], activities: [] };
  }
  return fs.readJsonSync(dataPath);
}

function saveData(data: DataFile): void {
  fs.ensureDirSync(CONFIG.dataDir);
  fs.writeJsonSync(getDataPath(), data, { spaces: 2 });
}

// === COMMANDS ===

async function cmdExport(args: string[]): Promise<void> {
  const outputIdx = args.findIndex((a) => a === "-o" || a === "--output");
  const outputFile = outputIdx >= 0 ? args[outputIdx + 1] : undefined;
  const toStdout = args.includes("--stdout");

  // Filter out -o and --output and their values to find projectId
  const filteredArgs = args.filter((a, i) => {
    if (a === "-o" || a === "--output") return false;
    if (i > 0 && (args[i - 1] === "-o" || args[i - 1] === "--output")) return false;
    if (a.startsWith("-")) return false;
    return true;
  });
  // Join remaining args to support project names with spaces
  const projectId = filteredArgs.length > 0 ? filteredArgs.join(" ") : undefined;

  const data = loadData();

  if (projectId) {
    // Export single project
    const project = data.projects.find((p) => p.id === projectId || p.name === projectId);
    if (!project) {
      log(`Project not found: ${projectId}`, "error");
      process.exit(1);
    }

    const bundle = {
      version: CONFIG.version,
      exportedAt: new Date().toISOString(),
      project,
    };

    if (toStdout) {
      console.log(JSON.stringify(bundle, null, 2));
    } else {
      const filename = outputFile || `${project.name.replace(/\s+/g, "-").toLowerCase()}.dss.json`;
      fs.writeJsonSync(filename, bundle, { spaces: 2 });
      log(`Exported to ${filename}`, "success");
    }
  } else {
    // Export all projects
    const bundle = {
      version: CONFIG.version,
      exportedAt: new Date().toISOString(),
      projects: data.projects,
      teams: data.teams,
    };

    if (toStdout) {
      console.log(JSON.stringify(bundle, null, 2));
    } else {
      const filename = outputFile || `dss-export-${timestamp()}.json`;
      fs.writeJsonSync(filename, bundle, { spaces: 2 });
      log(`Exported ${data.projects.length} projects to ${filename}`, "success");
    }
  }
}

async function cmdImport(args: string[]): Promise<void> {
  const file = args.find((a) => !a.startsWith("-"));
  const force = args.includes("-f") || args.includes("--force");

  if (!file) {
    log("Please specify a file to import", "error");
    process.exit(1);
  }

  if (!fs.existsSync(file)) {
    log(`File not found: ${file}`, "error");
    process.exit(1);
  }

  const bundle = fs.readJsonSync(file);
  const data = loadData();

  if (bundle.project) {
    // Single project import
    const existing = data.projects.find((p) => p.name === bundle.project.name);
    if (existing && !force) {
      log(`Project "${bundle.project.name}" already exists. Use --force to overwrite.`, "warn");
      process.exit(1);
    }

    if (existing) {
      Object.assign(existing, bundle.project, { updatedAt: new Date().toISOString() });
      log(`Updated existing project: ${bundle.project.name}`, "success");
    } else {
      const newProject = {
        ...bundle.project,
        id: `proj-${Date.now()}`,
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString(),
      };
      data.projects.push(newProject);
      log(`Imported project: ${bundle.project.name}`, "success");
    }
  } else if (bundle.projects) {
    // Multi-project import
    let imported = 0;
    let updated = 0;

    for (const project of bundle.projects) {
      const existing = data.projects.find((p) => p.name === project.name);
      if (existing) {
        if (force) {
          Object.assign(existing, project, { updatedAt: new Date().toISOString() });
          updated++;
        }
      } else {
        data.projects.push({
          ...project,
          id: `proj-${Date.now()}-${imported}`,
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString(),
        });
        imported++;
      }
    }

    log(`Imported ${imported} new projects, updated ${updated}`, "success");
  }

  saveData(data);
}

async function cmdBackup(args: string[]): Promise<void> {
  const outputIdx = args.findIndex((a) => a === "-o" || a === "--output");
  const outputDir = outputIdx >= 0 ? args[outputIdx + 1] : CONFIG.defaultBackupDir;

  fs.ensureDirSync(outputDir);

  const data = loadData();
  const backup = {
    version: CONFIG.version,
    createdAt: new Date().toISOString(),
    data,
  };

  const filename = path.join(outputDir, `dss-backup-${timestamp()}.json`);
  fs.writeJsonSync(filename, backup, { spaces: 2 });

  log(`Full backup created: ${filename}`, "success");
  log(`  Projects: ${data.projects.length}`, "info");
  log(`  Teams: ${data.teams.length}`, "info");
  log(`  Activities: ${data.activities.length}`, "info");
}

async function cmdRestore(args: string[]): Promise<void> {
  const file = args.find((a) => !a.startsWith("-"));
  const force = args.includes("-f") || args.includes("--force");

  if (!file) {
    log("Please specify a backup file to restore", "error");
    process.exit(1);
  }

  if (!fs.existsSync(file)) {
    log(`File not found: ${file}`, "error");
    process.exit(1);
  }

  const existingData = loadData();
  if (existingData.projects.length > 0 && !force) {
    log("Data already exists. Use --force to overwrite.", "warn");
    process.exit(1);
  }

  const backup = fs.readJsonSync(file);

  if (!backup.data && !backup.projects) {
    log("Invalid backup file format", "error");
    process.exit(1);
  }

  const data = backup.data || { projects: backup.projects, teams: backup.teams || [], activities: [] };
  saveData(data);

  log(`Restored from backup: ${file}`, "success");
  log(`  Projects: ${data.projects.length}`, "info");
  log(`  Teams: ${data.teams.length}`, "info");
}

async function cmdHealth(args: string[]): Promise<void> {
  const asJson = args.includes("--json");

  try {
    const response = await fetch(`${CONFIG.serverUrl}/health`);
    const health = (await response.json()) as { status: string; version: string; uptime: number };

    if (asJson) {
      console.log(JSON.stringify(health, null, 2));
    } else {
      log(`Server Status: ${health.status}`, health.status === "ok" ? "success" : "error");
      log(`Version: ${health.version}`, "info");
      log(`Uptime: ${health.uptime}s`, "info");
    }
  } catch {
    if (asJson) {
      console.log(JSON.stringify({ status: "offline", error: "Could not connect to server" }));
    } else {
      log("Server is offline or unreachable", "error");
      log(`Tried: ${CONFIG.serverUrl}/health`, "info");
    }
    process.exit(1);
  }
}

async function cmdList(): Promise<void> {
  const data = loadData();

  if (data.projects.length === 0) {
    log("No projects found", "info");
    return;
  }

  console.log("\nProjects:");
  console.log("─".repeat(60));

  for (const project of data.projects) {
    const tokens = project.tokens?.length || 0;
    const components = project.components?.length || 0;
    console.log(`  ${project.id}`);
    console.log(`    Name: ${project.name}`);
    console.log(`    Status: ${project.status}`);
    console.log(`    Tokens: ${tokens}, Components: ${components}`);
    console.log(`    Updated: ${project.updatedAt}`);
    console.log();
  }
}

// === MAIN ===

async function main(): Promise<void> {
  const args = process.argv.slice(2);
  const command = args[0];
  const cmdArgs = args.slice(1);

  if (!command || command === "help" || command === "--help" || command === "-h") {
    printHelp();
    process.exit(0);
  }

  switch (command) {
    case "export":
      await cmdExport(cmdArgs);
      break;
    case "import":
      await cmdImport(cmdArgs);
      break;
    case "backup":
      await cmdBackup(cmdArgs);
      break;
    case "restore":
      await cmdRestore(cmdArgs);
      break;
    case "health":
      await cmdHealth(cmdArgs);
      break;
    case "list":
    case "ls":
      await cmdList();
      break;
    default:
      log(`Unknown command: ${command}`, "error");
      printHelp();
      process.exit(1);
  }
}

main().catch((err) => {
  log(err.message, "error");
  process.exit(1);
});
