/**
 * Axint Eject Module
 *
 * Generates standalone Swift code that has no dependency on Axint.
 * This is useful for teams that want to verify there's no vendor lock-in
 * or for distributing intents without the compilation layer.
 *
 * The eject process:
 * 1. Compiles the TypeScript intent using the standard pipeline
 * 2. Transforms the output to remove Axint markers
 * 3. Optionally generates XCTest stubs
 * 4. Emits all files (Swift, plist, entitlements, test)
 */

import { compileSource } from "./compiler.js";
import { formatSwift } from "./format.js";
import type { IRIntent } from "./types.js";

// ─── Public Interface ────────────────────────────────────────────────

export interface EjectOptions {
  /** Output directory for ejected files (defaults to ".") */
  outDir?: string;
  /** Generate a basic XCTest file for the intent */
  includeTests?: boolean;
  /** Format output with swift-format (requires swift-format on PATH) */
  format?: boolean;
}

export interface EjectResult {
  /** The primary Swift file */
  swiftFile: {
    path: string;
    content: string;
  };
  /** Optional Info.plist fragment */
  infoPlist?: {
    path: string;
    content: string;
  };
  /** Optional entitlements fragment */
  entitlements?: {
    path: string;
    content: string;
  };
  /** Optional XCTest file */
  testFile?: {
    path: string;
    content: string;
  };
}

// ─── Main Eject Function ────────────────────────────────────────────

/**
 * Eject an intent from Axint, producing standalone Swift code with no
 * vendor lock-in. The resulting Swift code is production-ready and can
 * be committed directly to version control.
 *
 * @param source The TypeScript intent source
 * @param fileName Display name for diagnostics (e.g., "intent.ts")
 * @param options Eject options (outDir, includeTests, format)
 * @returns Ejected files
 */
export async function ejectIntent(
  source: string,
  fileName: string,
  options: EjectOptions = {}
): Promise<EjectResult> {
  // 1. Compile using the standard pipeline
  const compileResult = compileSource(source, fileName, {
    validate: true,
    emitInfoPlist: true,
    emitEntitlements: true,
  });

  if (!compileResult.success || !compileResult.output) {
    throw new Error(
      `Compilation failed: ${compileResult.diagnostics
        .filter((d) => d.severity === "error")
        .map((d) => d.message)
        .join("; ")}`
    );
  }

  const { ir, swiftCode, infoPlistFragment, entitlementsFragment } = compileResult.output;
  const outDir = options.outDir ?? ".";

  // 2. Transform Swift to remove Axint markers
  let ejectedSwift = transformSwiftForEject(swiftCode, ir);

  // 2b. Optionally run swift-format
  if (options.format) {
    const { formatted, ran } = await formatSwift(ejectedSwift);
    if (ran) ejectedSwift = formatted;
  }

  // 3. Construct file paths
  const intentFileName = `${ir.name}Intent.swift`;
  const swiftPath = `${outDir}/${intentFileName}`;
  const plistPath = infoPlistFragment
    ? `${outDir}/${ir.name}Intent.plist.fragment.xml`
    : null;
  const entitlementsPath = entitlementsFragment
    ? `${outDir}/${ir.name}Intent.entitlements.fragment.xml`
    : null;
  const testPath = options.includeTests ? `${outDir}/${ir.name}IntentTests.swift` : null;

  // 4. Build result object
  const result: EjectResult = {
    swiftFile: {
      path: swiftPath,
      content: ejectedSwift,
    },
  };

  if (infoPlistFragment && plistPath) {
    result.infoPlist = {
      path: plistPath,
      content: infoPlistFragment,
    };
  }

  if (entitlementsFragment && entitlementsPath) {
    result.entitlements = {
      path: entitlementsPath,
      content: entitlementsFragment,
    };
  }

  if (options.includeTests && testPath) {
    result.testFile = {
      path: testPath,
      content: generateTestFile(ir),
    };
  }

  return result;
}

// ─── Swift Transformation ───────────────────────────────────────────

/**
 * Transform generated Swift code to be "ejected" — remove Axint markers,
 * update the header comment, and enhance the TODO placeholder.
 */
function transformSwiftForEject(swiftCode: string, ir: IRIntent): string {
  const lines = swiftCode.split("\n");
  const result: string[] = [];

  let inHeader = true;

  for (let i = 0; i < lines.length; i++) {
    const line = lines[i];

    // Replace header comment
    if (inHeader && line.startsWith("// ")) {
      if (i === 0) {
        // First line: replace with intent name
        result.push(`// ${ir.name}Intent.swift`);
      } else if (
        line.includes("Generated by Axint") ||
        line.includes("Axint compiler output")
      ) {
        // Skip the Axint header
        result.push(
          `// Originally generated by Axint (https://github.com/agenticempire/axint)`
        );
      } else if (line.includes("Do not edit manually")) {
        // Skip the regeneration instruction
      } else {
        result.push(line);
      }
    } else if (line === "") {
      inHeader = false;
      result.push(line);
    } else {
      inHeader = false;

      // Replace the TODO comment with a more helpful one
      if (line.includes("// TODO: Implement your intent logic here.")) {
        result.push(`        // TODO: Implement your intent logic here.`);
        result.push(`        //`);
        result.push(`        // For more information about App Intents, see:`);
        result.push(`        // https://developer.apple.com/documentation/appintents`);
      } else {
        result.push(line);
      }
    }
  }

  return result.join("\n");
}

// ─── Test File Generation ───────────────────────────────────────────

/**
 * Generate a basic XCTest file for the intent.
 * This provides a scaffold that users can fill in with their own tests.
 */
function generateTestFile(ir: IRIntent): string {
  const lines: string[] = [];

  lines.push(`// ${ir.name}IntentTests.swift`);
  lines.push(`// Ejected from Axint`);
  lines.push(``);
  lines.push(`import XCTest`);
  lines.push(`import AppIntents`);
  lines.push(``);
  lines.push(`final class ${ir.name}IntentTests: XCTestCase {`);
  lines.push(``);
  lines.push(`    func testIntentInitialization() throws {`);
  lines.push(`        let intent = ${ir.name}Intent()`);
  lines.push(`        XCTAssertEqual(${ir.name}Intent.title.stringValue, "${ir.title}")`);
  lines.push(`    }`);
  lines.push(``);
  lines.push(`    func testIntentPerform() async throws {`);
  lines.push(`        let intent = ${ir.name}Intent()`);
  lines.push(`        // TODO: Set up intent parameters and test perform()`);
  lines.push(`        // let result = try await intent.perform()`);
  lines.push(`        // XCTAssertNotNil(result)`);
  lines.push(`    }`);
  lines.push(``);
  lines.push(`}`);
  lines.push(``);

  return lines.join("\n");
}
