/**
 * commit CLI execute — Execution logic, no validation
 */

import type { CommitSpec, CommitResult } from './types.js';
import { getCurrentBranch, getStatus, addFiles, commit, push, getLog, getDiffFiles } from '../lib/git.js';
import { detectBranchType } from '../lib/branch.js';
import { validateEfCore, decideEfcorePolicy } from '../lib/efcore.js';
import { readConfig } from '../lib/config.js';
import { detectVersion } from '../lib/version.js';

function generateCommitMessage(branch: string, version?: string): string {
  const type = detectBranchType(branch);
  let scope = branch.replace(/^(feature|release|hotfix)\//, '');
  scope = scope.split('-')[0];
  const tag = version ? `[v${version}] ` : '';

  switch (type) {
    case 'feature':
      return `feat(${scope}): ${tag}update files`;
    case 'hotfix':
      return `fix(${scope}): ${tag}update files`;
    case 'release':
      return `chore(${scope}): ${tag}update files`;
    default:
      return `chore: update files`;
  }
}

export async function execute(spec: CommitSpec, cwd?: string): Promise<CommitResult> {
  try {
    const branch = await getCurrentBranch(cwd);

    if (branch === 'main' || branch === 'master') {
      return {
        success: false,
        error: 'Cannot commit directly to main branch',
        branch,
        filesChanged: 0,
        pushed: false,
      };
    }

    if (branch === 'develop' || branch === 'development') {
      // warning, not blocker
    }

    const status = await getStatus(cwd);
    if (!status.dirty) {
      return {
        success: false,
        error: 'No changes to commit',
        branch,
        filesChanged: 0,
        pushed: false,
      };
    }

    let efcoreValidation = undefined;
    let efcoreWarnings: string[] = [];
    if (!spec.noEfcore) {
      const changedFiles = [...status.staged, ...status.modified, ...status.untracked];
      const validation = validateEfCore(changedFiles);
      efcoreValidation = validation;

      // Config-driven policy (restores 4.x): when a gitflow config exists its
      // efcore block drives blocking/confirmation (4.x defaults: all on). With NO
      // config we stay warn-only — don't impose gates outside a gitflow project.
      let efc: { enabled?: boolean; validateOnCommit?: boolean; blockDestructive?: boolean } =
        { enabled: false, validateOnCommit: false, blockDestructive: false };
      try {
        const cfg = await readConfig();
        if (cfg.efcore) efc = cfg.efcore;
      } catch {
        // no gitflow config → warn-only defaults above
      }

      const policy = decideEfcorePolicy(validation, efc);
      efcoreWarnings = policy.warnings;

      // Incomplete migration changeset → hard block (4.x STOP).
      if (policy.block) {
        return {
          success: false,
          error: policy.error,
          warnings: policy.warnings.length ? policy.warnings : undefined,
          branch,
          filesChanged: changedFiles.length,
          efcore: validation,
          pushed: false,
        };
      }

      // Destructive ops → ask for confirmation (4.x AskUserQuestion), NOT a silent
      // hard block. The skill prompts, then re-runs commit with confirmDestructive.
      if (policy.requiresConfirmation && !spec.confirmDestructive) {
        return {
          success: false,
          requiresConfirmation: true,
          error: policy.error,
          warnings: policy.warnings.length ? policy.warnings : undefined,
          branch,
          filesChanged: changedFiles.length,
          efcore: validation,
          pushed: false,
        };
      }

      if (policy.requiresConfirmation && spec.confirmDestructive) {
        efcoreWarnings = [
          ...efcoreWarnings,
          `EF Core: destructive operations confirmed — proceeding (${validation.destructiveOps.join(', ')}).`,
        ];
      }
    }

    const filesChanged = status.staged.length + status.modified.length + status.untracked.length;
    await addFiles(['.'], cwd);

    let version: string | undefined;
    if (!spec.message) {
      const projectDir = cwd || process.cwd();
      const versionInfo = await detectVersion(projectDir);
      if (versionInfo.current !== '0.0.0') {
        version = versionInfo.current;
      }
    }
    const message = spec.message || generateCommitMessage(branch, version);
    const commitResult = await commit(message, cwd);

    if (commitResult.exitCode !== 0) {
      return {
        success: false,
        error: `Commit failed: ${commitResult.stderr || commitResult.stdout}`,
        branch,
        filesChanged,
        efcore: efcoreValidation,
        pushed: false,
      };
    }

    let hash = '';
    try {
      const logs = await getLog(1, '%H', cwd);
      hash = logs[0] || '';
    } catch {
      hash = 'unknown';
    }

    let pushed = false;
    if (spec.push) {
      const pushResult = await push(branch, cwd);
      pushed = pushResult.exitCode === 0;
    }

    return {
      success: true,
      branch,
      commit: {
        hash,
        message,
      },
      filesChanged,
      efcore: efcoreValidation,
      warnings: efcoreWarnings.length ? efcoreWarnings : undefined,
      pushed,
    };
  } catch (err: unknown) {
    return {
      success: false,
      error: (err as Error).message,
      branch: '',
      filesChanged: 0,
      pushed: false,
    };
  }
}
