/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

import path from 'node:path';

import {
  snapshot,
  navigation,
  generateReport,
  zod,
  type Flags,
  type RunnerResult,
  type OutputMode,
} from '../third_party/index.js';

import {ToolCategory} from './categories.js';
import {startTrace} from './performance.js';
import {definePageTool} from './ToolDefinition.js';

export const lighthouseAudit = definePageTool({
  name: 'lighthouse_audit',
  description: `Get Lighthouse score and reports for accessibility, SEO, best practices, and agentic browsing. This excludes performance. For performance audits, run ${startTrace.name}`,
  annotations: {
    category: ToolCategory.DEBUGGING,
    readOnlyHint: false,
  },
  schema: {
    mode: zod
      .enum(['navigation', 'snapshot'])
      .default('navigation')
      .describe(
        '"navigation" reloads & audits. "snapshot" analyzes current state.',
      ),
    device: zod
      .enum(['desktop', 'mobile'])
      .default('desktop')
      .describe('Device to emulate.'),
    outputDirPath: zod
      .string()
      .optional()
      .describe('Directory for reports. If omitted, uses temporary files.'),
  },
  blockedByDialog: true,
  verifyFilesSchema: ['outputDirPath'],
  handler: async (request, response, context) => {
    const page = request.page;
    const categories = [
      'accessibility',
      'seo',
      'best-practices',
      'agentic-browsing',
    ];
    const formats = ['json', 'html'] as OutputMode[];
    const {
      mode = 'navigation',
      device = 'desktop',
      outputDirPath,
    } = request.params;

    const flags: Flags = {
      onlyCategories: categories,
      output: formats,
      // Default 30 second timeout for page load.
      maxWaitForLoad: 30_000,
    };

    if (device === 'desktop') {
      flags.formFactor = 'desktop';
      flags.screenEmulation = {
        mobile: false,
        width: 1350,
        height: 940,
        deviceScaleFactor: 1,
        disabled: false,
      };
    } else {
      flags.formFactor = 'mobile';
      flags.screenEmulation = {
        mobile: true,
        width: 412,
        height: 823,
        deviceScaleFactor: 1.75,
        disabled: false,
      };
    }

    let result: RunnerResult | undefined;
    try {
      if (mode === 'navigation') {
        result = await navigation(page.pptrPage, page.pptrPage.url(), {
          flags,
        });
      } else {
        result = await snapshot(page.pptrPage, {
          flags,
        });
      }

      if (!result) {
        throw new Error('Lighthouse audit failed.');
      }
    } finally {
      await page.restoreEmulation();
    }

    const lhr = result.lhr;
    const reportPaths: string[] = [];

    const encoder = new TextEncoder();
    const savePromises = formats.map(async format => {
      const report = generateReport(lhr, format);
      const data = encoder.encode(report);
      if (outputDirPath) {
        const reportPath = path.join(outputDirPath, `report`);
        const {filename} = await context.saveFile(
          data,
          reportPath,
          `.${format}`,
        );
        return filename;
      }
      const {filepath} = await context.saveTemporaryFile(
        data,
        `report.${format}`,
      );
      return filepath;
    });

    const results = await Promise.allSettled(savePromises);
    for (const res of results) {
      if (res.status === 'rejected') {
        throw res.reason;
      }
      reportPaths.push(res.value);
    }

    const categoryScores = Object.values(lhr.categories).map(c => ({
      id: c.id,
      title: c.title,
      score: c.score,
    }));

    const failedAudits = Object.values(lhr.audits).filter(
      a => a.score !== null && a.score < 1,
    ).length;

    const passedAudits = Object.values(lhr.audits).filter(
      a => a.score === 1,
    ).length;

    const output = {
      summary: {
        mode,
        device,
        url: lhr.mainDocumentUrl,
        scores: categoryScores,
        audits: {
          failed: failedAudits,
          passed: passedAudits,
        },
        timing: {
          total: lhr.timing.total,
        },
      },
      reports: reportPaths,
    };

    response.attachLighthouseResult(output);
  },
});
