const GitUtils = require('../utils/git');
const UI = require('../utils/ui');
const State = require('../utils/state');
const UpdateNotifier = require('../utils/update-notifier');

const FRAMEWORK_DIR = '.framework';

function fmtDate(iso) {
  if (!iso) return '—';
  try { return iso.slice(0, 10); } catch (_) { return iso; }
}

async function describeDrift(git, status) {
  // Uncommitted local edits to .framework/ (working tree, NOT commits)
  let uncommittedFiles = 0;
  try {
    const dirty = await git.git.raw(['diff', '--name-only', 'HEAD', '--', FRAMEWORK_DIR]);
    uncommittedFiles = dirty.trim() ? dirty.trim().split('\n').length : 0;
  } catch (_) { /* repo without HEAD yet */ }

  return {
    isAligned: status.isAligned,
    installedVersion: status.installedVersion,
    remoteVersion: status.remoteVersion,
    behind: status.commitCount.behind,
    ahead: status.commitCount.ahead,
    uncommittedFiles,
    hasFetchHead: status.hasFetchHead,
  };
}

// Emit a structured JSON object on stdout (v3.32.0+). Used by agents / CI to
// avoid scraping the human-readable box with fragile regex. Schema is
// versioned (`baldart.version/1`) so consumers can detect breaking changes.
function emitVersionJson(obj) {
  process.stdout.write(JSON.stringify({ schema: 'baldart.version/1', ...obj }) + '\n');
}

async function version(opts = {}) {
  const git = new GitUtils();
  const jsonMode = opts.json === true;
  if (jsonMode) UI.setJsonMode(true);

  try {
    const exists = await git.frameworkExists();
    if (!exists) {
      if (jsonMode) {
        emitVersionJson({ installed: false, error: 'framework_not_installed' });
        process.exit(0);
      }
      UI.warning('Framework not installed');
      UI.info('Install with: npx baldart add');
      process.exit(0);
    }

    const frameworkVersion = await git.getFrameworkVersion();
    const state = State.load();
    const cliVersion = require('../../package.json').version;
    const cliUpdate = UpdateNotifier.hint({ currentVersion: cliVersion });
    const verbose = opts.verbose === true;

    // SSOT (v3.25.0+): one query, version-compare authority.
    const repo = state.framework_repo || State.DEFAULT_REPO;
    let status;
    if (opts.offline === true) {
      status = {
        installedVersion: frameworkVersion,
        remoteVersion: 'unknown',
        isAligned: false,
        commitCount: { behind: 0, ahead: 0, note: '' },
        aheadScoped: 0, hasDivergence: false, hasFetchHead: false, fetched: false,
      };
    } else {
      status = await git.getUpdateStatus(repo);
    }
    const drift = await describeDrift(git, status);

    // Machine-readable path (v3.32.0+) — emit the structured object and stop.
    // Mirrors exactly the facts the human box shows below, so the autonomous
    // /baldart-update skill can read fields instead of regex-scraping prose.
    if (jsonMode) {
      emitVersionJson({
        installed: true,
        installed_version: frameworkVersion,
        remote_version: drift.remoteVersion, // string version, or 'unknown'
        aligned: drift.isAligned,
        offline: opts.offline === true || !drift.hasFetchHead,
        fetched: drift.hasFetchHead,
        uncommitted_files: drift.uncommittedFiles,
        commits_ahead: drift.ahead,
        commits_behind: drift.behind,
        cli_version: cliVersion,
        cli_latest: cliUpdate ? cliUpdate.latest : null,
        cli_update_available: !!cliUpdate,
        repository: `https://github.com/${repo}`,
        last_update_date: state.last_update_date || null,
        last_pushed_version: state.last_pushed_version || null,
        last_push_date: state.last_push_date || null,
      });
      return;
    }

    // Messaging based on `isAligned` (not commit count, which is subtree noise).
    let remoteLine;
    if (!drift.hasFetchHead) {
      remoteLine = 'Remote:      (offline / not fetched)';
    } else if (drift.isAligned) {
      remoteLine = `Remote:      v${drift.remoteVersion} (aligned)`;
    } else if (drift.remoteVersion !== 'unknown') {
      remoteLine = `Remote:      v${drift.remoteVersion} (installed v${drift.installedVersion} — \`npx baldart update\`)`;
    } else {
      remoteLine = 'Remote:      (upstream VERSION unreadable)';
    }

    const lines = [
      `Installed:   v${frameworkVersion}    ${state.install_date ? `(since ${fmtDate(state.install_date)})` : ''}`.trim(),
      remoteLine,
      drift.uncommittedFiles > 0
        ? `Local:       v${frameworkVersion} + ${drift.uncommittedFiles} uncommitted file(s) in .framework/`
        : `Local:       v${frameworkVersion} (clean)`,
      '',
      `Last update: ${fmtDate(state.last_update_date)}`,
      `Last push:   v${state.last_pushed_version || '—'}    ${state.last_push_date ? `(${fmtDate(state.last_push_date)})` : ''}`.trim(),
      '',
      cliUpdate
        ? `CLI:         v${cliVersion} → v${cliUpdate.latest} available (npm i -g baldart@latest)`
        : `CLI:         v${cliVersion}`,
      `Repository:  https://github.com/${repo}`,
    ];

    if (verbose && drift.hasFetchHead) {
      lines.push('');
      lines.push('Diagnostics (subtree commit history — includes auto-generated merges):');
      lines.push(`  commits ahead:  ${drift.ahead}`);
      lines.push(`  commits behind: ${drift.behind}`);
      lines.push('  (these counts grow with every `git subtree pull --squash`; ignore unless investigating)');
    }

    UI.newline();
    UI.box('BALDART VERSION', lines.filter((l) => l !== ''));
    UI.newline();
  } catch (error) {
    if (jsonMode) {
      emitVersionJson({ installed: null, error: 'version_failed', reason: error.message });
      process.exit(1);
    }
    UI.error(`Failed to get version: ${error.message}`);
    process.exit(1);
  }
}

module.exports = version;
