'use strict';

const fs = require('fs');
const path = require('path');

function getClaudeJsonPath(homeDir = process.env.HOME) {
  return path.join(homeDir, '.claude.json');
}

function getGlobalSettingsPath(homeDir = process.env.HOME) {
  return path.join(homeDir, '.claude', 'settings.local.json');
}

function readJsonFile(filePath) {
  try {
    return JSON.parse(fs.readFileSync(filePath, 'utf8'));
  } catch {
    return {};
  }
}

async function readJsonFileAsync(filePath) {
  try {
    return JSON.parse(await fs.promises.readFile(filePath, 'utf8'));
  } catch {
    return {};
  }
}

function writeJsonFile(filePath, data) {
  const dir = path.dirname(filePath);
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
  fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
}

async function writeJsonFileAsync(filePath, data) {
  const dir = path.dirname(filePath);
  await fs.promises.mkdir(dir, { recursive: true });
  await fs.promises.writeFile(filePath, JSON.stringify(data, null, 2));
}

function getProjectSettingsPath(projectPath) {
  return path.join(projectPath, '.claude', 'settings.local.json');
}

function isValidPermRule(rule) {
  const m = String(rule || '').match(/^Bash\(.*:\*(.*)?\)$/);
  if (m && m[1]) return false;
  return true;
}

function pushSettingsRules(rules, settings, { scope, project }) {
  const permissions = settings && settings.permissions || {};
  for (const tool of permissions.allow || []) {
    if (!isValidPermRule(tool)) {
      console.warn(`  Skipping invalid perm rule: ${tool}`);
      continue;
    }
    rules.push({ rule: tool, listType: 'allow', scope, project });
  }
  for (const tool of permissions.deny || []) {
    if (!isValidPermRule(tool)) continue;
    rules.push({ rule: tool, listType: 'deny', scope, project });
  }
}

function getSettingsAllowList(settingsPath) {
  const settings = readJsonFile(settingsPath);
  return (settings.permissions && settings.permissions.allow) || [];
}

function getSettingsDenyList(settingsPath) {
  const settings = readJsonFile(settingsPath);
  return (settings.permissions && settings.permissions.deny) || [];
}

function collectJsonRules({ homeDir = process.env.HOME } = {}) {
  const rules = [];
  const globalSettingsPath = getGlobalSettingsPath(homeDir);
  const claudeJsonPath = getClaudeJsonPath(homeDir);

  pushSettingsRules(rules, readJsonFile(globalSettingsPath), { scope: 'global', project: '__global__' });

  const claudeJson = readJsonFile(claudeJsonPath);
  const projectPaths = Object.keys(claudeJson.projects || {});
  for (const projectPath of projectPaths) {
    const settingsPath = getProjectSettingsPath(projectPath);
    pushSettingsRules(rules, readJsonFile(settingsPath), { scope: 'project', project: projectPath });
  }

  for (const [projectPath, projectData] of Object.entries(claudeJson.projects || {})) {
    const allowedTools = projectData.allowedTools || [];
    for (const tool of allowedTools) {
      if (!isValidPermRule(tool)) continue;
      rules.push({ rule: tool, listType: 'allow', scope: 'project', project: projectPath });
    }
  }

  return rules;
}

async function collectJsonRulesAsync({ homeDir = process.env.HOME } = {}) {
  const rules = [];
  const globalSettingsPath = getGlobalSettingsPath(homeDir);
  const claudeJsonPath = getClaudeJsonPath(homeDir);

  pushSettingsRules(rules, await readJsonFileAsync(globalSettingsPath), { scope: 'global', project: '__global__' });

  const claudeJson = await readJsonFileAsync(claudeJsonPath);
  const projectPaths = Object.keys(claudeJson.projects || {});
  for (const projectPath of projectPaths) {
    const settingsPath = getProjectSettingsPath(projectPath);
    pushSettingsRules(rules, await readJsonFileAsync(settingsPath), { scope: 'project', project: projectPath });
  }

  for (const [projectPath, projectData] of Object.entries(claudeJson.projects || {})) {
    const allowedTools = projectData.allowedTools || [];
    for (const tool of allowedTools) {
      if (!isValidPermRule(tool)) continue;
      rules.push({ rule: tool, listType: 'allow', scope: 'project', project: projectPath });
    }
  }

  return rules;
}

function mergeJsonRulesIntoDb(dbApi, options = {}) {
  const jsonRules = collectJsonRules(options);
  const before = dbApi.listPermRules().length;
  for (const rule of jsonRules) dbApi.addPermRule(rule);
  const imported = dbApi.listPermRules().length - before;
  if (imported > 0) {
    console.log(`  Merged ${imported} new permission rules from JSON files into DB`);
  }
  return imported;
}

async function mergeJsonRulesIntoDbAsync(dbApi, options = {}) {
  const jsonRules = await collectJsonRulesAsync(options);
  return applyJsonRulesToDb(dbApi, jsonRules, { mergeOnly: true }).imported;
}

function applyJsonRulesToDb(dbApi, jsonRules, { mergeOnly = false } = {}) {
  const rules = Array.isArray(jsonRules) ? jsonRules : [];
  const existing = dbApi.listPermRules();
  if (existing.length > 0 || mergeOnly) {
    const before = existing.length;
    for (const rule of rules) dbApi.addPermRule(rule);
    const imported = dbApi.listPermRules().length - before;
    if (imported > 0) {
      console.log(`  Merged ${imported} new permission rules from JSON files into DB`);
    }
    return { imported, mode: 'merge' };
  }

  if (rules.length > 0) {
    dbApi.bulkSetPermRules(rules);
    console.log(`  Imported ${rules.length} permission rules from JSON files into DB`);
    return { imported: rules.length, mode: 'bulk' };
  }
  return { imported: 0, mode: 'empty' };
}

function syncDbToJsonFiles(dbApi, { homeDir = process.env.HOME } = {}) {
  for (const rule of collectJsonRules({ homeDir })) dbApi.addPermRule(rule);
  for (const row of dbApi.listPermRules()) {
    if (!isValidPermRule(row.rule)) {
      console.warn(`  Removing invalid perm rule from DB: ${row.rule}`);
      dbApi.removePermRule({ rule: row.rule, listType: row.list_type, project: row.project });
    }
  }

  const byProject = dbApi.getPermRulesByProject();
  const globalSettingsPath = getGlobalSettingsPath(homeDir);
  const globalRules = byProject.__global__ || { allow: [], deny: [] };
  const globalSettings = readJsonFile(globalSettingsPath);
  if (!globalSettings.permissions) globalSettings.permissions = {};
  globalSettings.permissions.allow = globalRules.allow.filter(isValidPermRule);
  globalSettings.permissions.deny = globalRules.deny.filter(isValidPermRule);
  writeJsonFile(globalSettingsPath, globalSettings);

  for (const [project, lists] of Object.entries(byProject)) {
    if (project === '__global__') continue;
    const settingsPath = getProjectSettingsPath(project);
    try {
      const settings = readJsonFile(settingsPath);
      if (!settings.permissions) settings.permissions = {};
      settings.permissions.allow = lists.allow.filter(isValidPermRule);
      settings.permissions.deny = lists.deny.filter(isValidPermRule);
      writeJsonFile(settingsPath, settings);
    } catch {}
  }
}

async function syncDbToJsonFilesAsync(dbApi, { homeDir = process.env.HOME } = {}) {
  for (const rule of await collectJsonRulesAsync({ homeDir })) dbApi.addPermRule(rule);
  for (const row of dbApi.listPermRules()) {
    if (!isValidPermRule(row.rule)) {
      console.warn(`  Removing invalid perm rule from DB: ${row.rule}`);
      dbApi.removePermRule({ rule: row.rule, listType: row.list_type, project: row.project });
    }
  }

  const byProject = dbApi.getPermRulesByProject();
  const globalSettingsPath = getGlobalSettingsPath(homeDir);
  const globalRules = byProject.__global__ || { allow: [], deny: [] };
  const globalSettings = await readJsonFileAsync(globalSettingsPath);
  if (!globalSettings.permissions) globalSettings.permissions = {};
  globalSettings.permissions.allow = globalRules.allow.filter(isValidPermRule);
  globalSettings.permissions.deny = globalRules.deny.filter(isValidPermRule);
  await writeJsonFileAsync(globalSettingsPath, globalSettings);

  for (const [project, lists] of Object.entries(byProject)) {
    if (project === '__global__') continue;
    const settingsPath = getProjectSettingsPath(project);
    try {
      const settings = await readJsonFileAsync(settingsPath);
      if (!settings.permissions) settings.permissions = {};
      settings.permissions.allow = lists.allow.filter(isValidPermRule);
      settings.permissions.deny = lists.deny.filter(isValidPermRule);
      await writeJsonFileAsync(settingsPath, settings);
    } catch {}
  }
}

function importPermissionsToDb(dbApi, { syncBack = true, homeDir = process.env.HOME } = {}) {
  const existing = dbApi.listPermRules();
  if (existing.length > 0) {
    mergeJsonRulesIntoDb(dbApi, { homeDir });
    if (syncBack) syncDbToJsonFiles(dbApi, { homeDir });
    return;
  }

  const rules = collectJsonRules({ homeDir });
  if (rules.length > 0) {
    dbApi.bulkSetPermRules(rules);
    console.log(`  Imported ${rules.length} permission rules from JSON files into DB`);
    if (syncBack) syncDbToJsonFiles(dbApi, { homeDir });
  }
}

async function importPermissionsToDbAsync(dbApi, {
  syncBack = true,
  homeDir = process.env.HOME,
  applyWith = null,
  applyRulesWith = null,
} = {}) {
  const rules = await collectJsonRulesAsync({ homeDir });
  const apply = () => applyJsonRulesToDb(dbApi, rules);
  const result = typeof applyRulesWith === 'function'
    ? await applyRulesWith(rules, apply)
    : (typeof applyWith === 'function' ? await applyWith(apply) : apply());
  if (syncBack) await syncDbToJsonFilesAsync(dbApi, { homeDir });
  return result;
}

module.exports = {
  applyJsonRulesToDb,
  collectJsonRules,
  collectJsonRulesAsync,
  getClaudeJsonPath,
  getGlobalSettingsPath,
  getProjectSettingsPath,
  importPermissionsToDb,
  importPermissionsToDbAsync,
  isValidPermRule,
  mergeJsonRulesIntoDb,
  mergeJsonRulesIntoDbAsync,
  readJsonFile,
  readJsonFileAsync,
  syncDbToJsonFiles,
  syncDbToJsonFilesAsync,
  writeJsonFile,
  writeJsonFileAsync,
};
