import { execFile } from 'child_process';
import { promisify } from 'util';

const execFileAsync = promisify(execFile);

type UnknownRecord = Record<string, unknown>;
type CommandExistsFn = (command: string) => Promise<boolean>;

const asRecord = (value: unknown): UnknownRecord | undefined => (
  value && typeof value === 'object' ? value as UnknownRecord : undefined
);

async function commandExists(command: string): Promise<boolean> {
  try {
    await execFileAsync('sh', ['-lc', `command -v ${command} >/dev/null 2>&1`]);
    return true;
  } catch {
    return false;
  }
}

export interface MinioExtractorStatus {
  minioEnabled: boolean;
  pdfSupported: boolean;
  docxSupported: boolean;
  availableDocxExtractors: string[];
  missingRequirements: string[];
}

export interface MinioExtractorHintOptions {
  platform?: NodeJS.Platform;
  exists?: CommandExistsFn;
}

export async function checkMinioExtractorStatus(
  config: Record<string, unknown>,
  exists: CommandExistsFn = commandExists,
): Promise<MinioExtractorStatus> {
  const backup = asRecord(config.backup);
  const minio = asRecord(backup?.minio);
  const minioEnabled = minio?.enabled === true;

  if (!minioEnabled) {
    return {
      minioEnabled: false,
      pdfSupported: false,
      docxSupported: false,
      availableDocxExtractors: [],
      missingRequirements: [],
    };
  }

  const [hasPdfToText, hasPandoc, hasDocx2Txt] = await Promise.all([
    exists('pdftotext'),
    exists('pandoc'),
    exists('docx2txt'),
  ]);

  const availableDocxExtractors = [
    ...(hasPandoc ? ['pandoc'] : []),
    ...(hasDocx2Txt ? ['docx2txt'] : []),
  ];
  const pdfSupported = hasPdfToText;
  const docxSupported = availableDocxExtractors.length > 0;

  const missingRequirements: string[] = [];
  if (!pdfSupported) {
    missingRequirements.push('pdftotext');
  }
  if (!docxSupported) {
    missingRequirements.push('pandoc or docx2txt');
  }

  return {
    minioEnabled,
    pdfSupported,
    docxSupported,
    availableDocxExtractors,
    missingRequirements,
  };
}

export function summarizeMinioExtractorStatus(status: MinioExtractorStatus): string {
  const pdf = status.pdfSupported ? 'pdf:ok(pdftotext)' : 'pdf:missing(pdftotext)';
  const docx = status.docxSupported
    ? `docx:ok(${status.availableDocxExtractors.join('|')})`
    : 'docx:missing(pandoc|docx2txt)';
  return `${pdf}, ${docx}`;
}

export function renderMinioExtractorSetupLines(status: MinioExtractorStatus): string[] {
  if (!status.minioEnabled) {
    return [];
  }

  const lines: string[] = [
    'MinIO ingestion extractor requirements:',
    `  PDF (.pdf): pdftotext ${status.pdfSupported ? 'detected' : 'missing'}`,
    `  DOCX (.docx): pandoc or docx2txt ${status.docxSupported ? `detected (${status.availableDocxExtractors.join(', ')})` : 'missing'}`,
  ];

  if (status.missingRequirements.length > 0) {
    lines.push('  Missing extractors will limit PDF/DOCX ingestion for minio.ingest and minio.sync.');
    lines.push('  Install missing tools, then run `flynn doctor` to verify.');
  } else {
    lines.push('  All extractor dependencies detected.');
  }

  return lines;
}

function hasMissingPdf(status: MinioExtractorStatus): boolean {
  return !status.pdfSupported;
}

function hasMissingDocx(status: MinioExtractorStatus): boolean {
  return !status.docxSupported;
}

function packageListFor(manager: string, status: MinioExtractorStatus): string[] {
  const packages: string[] = [];
  if (hasMissingPdf(status)) {
    const pdfMap: Record<string, string> = {
      'apt-get': 'poppler-utils',
      dnf: 'poppler-utils',
      pacman: 'poppler',
      zypper: 'poppler-tools',
      apk: 'poppler-utils',
    };
    if (pdfMap[manager]) {
      packages.push(pdfMap[manager]);
    }
  }
  if (hasMissingDocx(status)) {
    packages.push('pandoc');
  }
  return Array.from(new Set(packages));
}

export async function getMinioExtractorInstallHints(
  status: MinioExtractorStatus,
  options: MinioExtractorHintOptions = {},
): Promise<string[]> {
  if (!status.minioEnabled || status.missingRequirements.length === 0) {
    return [];
  }

  const platform = options.platform ?? process.platform;
  const exists = options.exists ?? commandExists;

  if (platform === 'darwin') {
    const packages: string[] = [];
    if (hasMissingPdf(status)) {
      packages.push('poppler');
    }
    if (hasMissingDocx(status)) {
      packages.push('pandoc');
    }
    return [`brew install ${Array.from(new Set(packages)).join(' ')}`];
  }

  if (platform === 'linux') {
    const managers = ['apt-get', 'dnf', 'pacman', 'zypper', 'apk'];
    for (const manager of managers) {
      if (!(await exists(manager))) {
        continue;
      }
      const packages = packageListFor(manager, status);
      if (packages.length > 0) {
        if (manager === 'pacman') {
          return [`sudo pacman -S --needed ${packages.join(' ')}`];
        }
        return [`sudo ${manager} install -y ${packages.join(' ')}`];
      }
    }
  }

  return ['Install pdftotext (poppler) and pandoc/docx2txt using your system package manager'];
}
