import fs from 'fs-extra';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';

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

export function getContentDir() {
  return join(__dirname, '..', '..', 'content');
}

export async function copyDirectory(src, dest, options = {}) {
  const { filter, overwrite = true } = options;

  await fs.copy(src, dest, {
    overwrite,
    filter: filter || (() => true),
    errorOnExist: false
  });
}

export async function copyFile(src, dest) {
  await fs.ensureDir(dirname(dest));
  await fs.copy(src, dest, { overwrite: true });
}

export async function pathExists(path) {
  return fs.pathExists(path);
}

export async function readJson(path) {
  return fs.readJson(path);
}

export async function writeJson(path, data) {
  await fs.ensureDir(dirname(path));
  await fs.writeJson(path, data, { spaces: 2 });
}

export async function ensureDir(path) {
  await fs.ensureDir(path);
}

export async function writeFile(path, content) {
  await fs.ensureDir(dirname(path));
  await fs.writeFile(path, content, 'utf8');
}

export async function removeDir(path) {
  await fs.remove(path);
}

export async function readFile(path) {
  return fs.readFile(path, 'utf8');
}

/**
 * Create symlink with fallback to copy if symlink fails
 * @param {string} target - Path to the original file/folder
 * @param {string} link - Path where symlink should be created
 * @param {string} type - 'file' or 'dir'
 * @returns {Promise<'symlink' | 'copy'>} - Returns how the link was created
 */
export async function createSymlink(target, link, type = 'file') {
  await fs.ensureDir(dirname(link));

  try {
    // Try to create symlink
    await fs.ensureSymlink(target, link, type);
    return 'symlink';
  } catch (error) {
    // Fallback: copy the file/directory if symlink fails
    // (Windows may require admin permissions for symlinks)
    if (type === 'file') {
      await copyFile(target, link);
    } else {
      await copyDirectory(target, link);
    }
    return 'copy';
  }
}

export async function updateGitignore(projectPath) {
  const gitignorePath = join(projectPath, '.gitignore');

  const morphRules = [
    '',
    '# MORPH-SPEC',
    '.morph/examples/',
    '.morph/templates/',
    '.claude/settings.local.json',
    ''
  ];

  let content = '';
  let hasMorphSection = false;

  // Read existing .gitignore if it exists
  if (await pathExists(gitignorePath)) {
    content = await readFile(gitignorePath);
    hasMorphSection = content.includes('# MORPH-SPEC');
  }

  // If MORPH section already exists, check if rules are up to date
  if (hasMorphSection) {
    const lines = content.split('\n');
    const morphStartIndex = lines.findIndex(line => line.trim() === '# MORPH-SPEC');

    // Find the end of MORPH section (next empty line or section header)
    let morphEndIndex = morphStartIndex + 1;
    while (morphEndIndex < lines.length &&
           lines[morphEndIndex].trim() !== '' &&
           !lines[morphEndIndex].startsWith('#')) {
      morphEndIndex++;
    }

    // Extract current MORPH rules
    const currentMorphRules = lines.slice(morphStartIndex, morphEndIndex + 1);
    const expectedMorphRules = morphRules.slice(1, -1); // Remove empty lines from comparison

    // Check if all expected rules are present
    const missingRules = expectedMorphRules.filter(rule =>
      rule.startsWith('#') || !currentMorphRules.some(line => line.trim() === rule)
    );

    if (missingRules.length > 0) {
      // Update the section with all rules
      lines.splice(morphStartIndex, morphEndIndex - morphStartIndex, ...morphRules.slice(1, -1));
      content = lines.join('\n');
    }
  } else {
    // Add MORPH section
    if (content && !content.endsWith('\n')) {
      content += '\n';
    }
    content += morphRules.join('\n');
  }

  await writeFile(gitignorePath, content);
}
