import { readFile, writeFile, copyFile } from 'fs/promises';
import { env } from '../../config/env';
import { logger } from '../../utils/logger';
import { parseDocument, Document } from 'yaml';
import type { ScalarTag } from 'yaml';

/**
 * Custom YAML tag schema to preserve !!python/name: and !!python/object: tags.
 * Without this, the yaml library would reject these custom tags.
 */
const pythonNameTag: ScalarTag = {
  identify: () => false,
  tag: '!python/name',
  collection: undefined as never,
  resolve: (str: string) => str,
};

const pythonObjectTag: ScalarTag = {
  identify: () => false,
  tag: '!python/object',
  collection: undefined as never,
  resolve: (str: string) => str,
};

/**
 * Parse mkdocs.yml content with support for !!python/name: tags.
 * Returns a yaml Document that preserves comments and formatting.
 */
function parseConfig(content: string): Document {
  return parseDocument(content, {
    customTags: (tags) => [
      ...tags,
      pythonNameTag,
      pythonObjectTag,
    ],
    keepSourceTokens: true,
  });
}

/**
 * Validate that a string is valid YAML (with python tags support).
 * Returns null if valid, error message if invalid.
 */
function validateYaml(content: string): string | null {
  try {
    const doc = parseConfig(content);
    const errors = doc.errors;
    if (errors.length > 0) {
      return errors.map(e => e.message).join('; ');
    }
    return null;
  } catch (err) {
    return (err as Error).message;
  }
}

async function readConfig(): Promise<string> {
  return readFile(env.MKDOCS_CONFIG_PATH, 'utf-8');
}

async function writeConfig(content: string): Promise<void> {
  // Validate YAML first
  const error = validateYaml(content);
  if (error) {
    throw new Error(`Invalid YAML: ${error}`);
  }

  // Create backup
  try {
    await copyFile(env.MKDOCS_CONFIG_PATH, `${env.MKDOCS_CONFIG_PATH}.bak`);
  } catch {
    logger.warn('Could not create backup of mkdocs.yml');
  }

  await writeFile(env.MKDOCS_CONFIG_PATH, content, 'utf-8');
}

/**
 * Trigger `mkdocs build --clean` via the build trigger HTTP server
 * running inside the MkDocs container on port 8001.
 */
async function triggerBuild(): Promise<{ success: boolean; output: string; duration: number }> {
  const buildUrl = `${env.MKDOCS_PREVIEW_URL.replace(':8000', ':8001')}/build`;
  const startTime = Date.now();

  try {
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), 130_000);

    const response = await fetch(buildUrl, {
      method: 'POST',
      signal: controller.signal,
    });
    clearTimeout(timeout);

    const data = await response.json() as { success: boolean; output: string; duration: number };
    return data;
  } catch (err) {
    const duration = Date.now() - startTime;
    logger.error('MkDocs build failed', err);
    return {
      success: false,
      output: `Build error: ${(err as Error).message}`,
      duration,
    };
  }
}

export const mkdocsConfigService = {
  readConfig,
  writeConfig,
  validateYaml,
  parseConfig,
  triggerBuild,
};
