import * as fs from 'node:fs';
import * as path from 'node:path';
import { validateManifestForMxCli } from '../utils/manifestValidator.js';
import { readManifestFromPackageDir } from '../utils/package-store.js';

type JsonRecord = Record<string, unknown>;

export interface IValidateCommandOptions {
  readonly cwd?: string;
}

export interface IValidateCommandResult {
  readonly packageName: string;
  readonly valid: boolean;
  readonly diagnostics: ReadonlyArray<{ code: string; message: string; field?: string; componentIndex?: number }>;
}

function readJsonObject(filePath: string): JsonRecord {
  const raw = fs.readFileSync(filePath, 'utf8').replace(/^\uFEFF/, '');
  const parsed = JSON.parse(raw) as unknown;
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
    throw new Error(`${filePath} must contain a JSON object`);
  }
  return parsed as JsonRecord;
}

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

function resolveManifest(validationTarget: string): { packageName: string; manifest: JsonRecord } {
  const targetStat = fs.statSync(validationTarget);
  if (targetStat.isDirectory()) {
    return readManifestFromPackageDir(validationTarget);
  }

  const normalized = path.basename(validationTarget).toLowerCase();
  if (normalized === 'matrix.json') {
    const manifest = readJsonObject(validationTarget);
    const packageName = typeof manifest.name === 'string' ? manifest.name.trim() : '';
    if (!packageName) {
      throw new Error(`matrix.json must contain a non-empty "name" field (${validationTarget})`);
    }
    return { packageName, manifest };
  }

  if (normalized === 'package.json') {
    const pkg = readJsonObject(validationTarget);
    const packageName = typeof pkg.name === 'string' ? pkg.name.trim() : '';
    if (!packageName) {
      throw new Error(`package.json must contain a non-empty "name" field (${validationTarget})`);
    }
    const matrix = asRecord(pkg.matrix) ?? {};
    return {
      packageName,
      manifest: {
        name: packageName,
        version: pkg.version,
        runtime: matrix.runtime,
        components: matrix.components,
        permissions: matrix.permissions,
      },
    };
  }

  throw new Error('Validation target must be a directory, matrix.json, or package.json');
}

export async function validateCommand(
  target: string | undefined,
  options: IValidateCommandOptions = {}
): Promise<IValidateCommandResult> {
  const cwd = path.resolve(options.cwd ?? process.cwd());
  const resolvedTarget = path.resolve(cwd, target ?? '.');
  if (!fs.existsSync(resolvedTarget)) {
    throw new Error(`Validation target does not exist: ${resolvedTarget}`);
  }

  const { packageName, manifest } = resolveManifest(resolvedTarget);
  const result = validateManifestForMxCli(manifest);
  return {
    packageName,
    valid: result.valid,
    diagnostics: result.diagnostics.map((entry) => ({
      code: entry.code,
      message: entry.message,
      field: entry.field,
      componentIndex: entry.componentIndex,
    })),
  };
}
