import * as fs from 'node:fs';
import * as path from 'node:path';
import {
  packageNameToPath,
  resolvePackagesNodeModulesRoot,
  resolveRegistryRoot,
} from '../utils/package-store.js';
import { resolveRegistryTarball } from '../utils/registry-store.js';
import { compareSemver } from '../utils/versioning.js';

type JsonRecord = Record<string, unknown>;

function asRecord(value: unknown): JsonRecord | null {
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
    return null;
  }
  return value as JsonRecord;
}

interface IForkRecord {
  origin: string;
  originVersion: string;
  [key: string]: unknown;
}

function readForkRecord(filePath: string): IForkRecord {
  if (!fs.existsSync(filePath)) {
    throw new Error(`Fork metadata missing: ${filePath}`);
  }
  const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8')) as unknown;
  const record = asRecord(parsed);
  const origin = typeof record?.origin === 'string' ? record.origin.trim() : '';
  const originVersion = typeof record?.originVersion === 'string' ? record.originVersion.trim() : '';
  if (!origin || !originVersion) {
    throw new Error(`Invalid fork metadata in ${filePath}`);
  }
  return {
    ...(record ?? {}),
    origin,
    originVersion,
  };
}

function writeForkRecord(filePath: string, payload: IForkRecord): void {
  fs.writeFileSync(filePath, JSON.stringify(payload, null, 2), 'utf8');
}

export interface IUpdateForkCommandOptions {
  readonly cwd?: string;
  readonly packagesDir?: string;
  readonly registryDir?: string;
  readonly version?: string;
}

export interface IUpdateForkCommandResult {
  readonly packageName: string;
  readonly origin: string;
  readonly previousOriginVersion: string;
  readonly nextOriginVersion: string;
  readonly updated: boolean;
}

export async function updateForkCommand(
  packageName: string,
  options: IUpdateForkCommandOptions = {},
): Promise<IUpdateForkCommandResult> {
  const cwd = path.resolve(options.cwd ?? process.cwd());
  const nodeModulesRoot = resolvePackagesNodeModulesRoot(cwd, options.packagesDir);
  const registryRoot = resolveRegistryRoot(cwd, options.registryDir);
  const packageDir = path.join(nodeModulesRoot, packageNameToPath(packageName));
  if (!fs.existsSync(packageDir)) {
    throw new Error(`Package is not installed: ${packageName}`);
  }

  const forkRecordPath = path.join(packageDir, '.forked-from');
  const forkRecord = readForkRecord(forkRecordPath);
  const upstream = resolveRegistryTarball(registryRoot, forkRecord.origin, options.version);
  const previousOriginVersion = forkRecord.originVersion;
  const nextOriginVersion = upstream.version;
  const updated = compareSemver(nextOriginVersion, previousOriginVersion) > 0;

  if (updated) {
    forkRecord.originVersion = nextOriginVersion;
    forkRecord.updatedAt = new Date().toISOString();
    writeForkRecord(forkRecordPath, forkRecord);
  }

  return {
    packageName,
    origin: forkRecord.origin,
    previousOriginVersion,
    nextOriginVersion,
    updated,
  };
}
