import { Scanner, ScanResult, ScanOptions } from '../types';
import { RULES } from '../rules';
import { glob } from 'glob';
import { readFileSync } from 'fs';
import https from 'https';
import http from 'http';

export class LinkScanner implements Scanner {
  name = 'Broken Links Scanner';

  async scan(options: ScanOptions): Promise<ScanResult[]> {
    const results: ScanResult[] = [];
    // Find links in source files and check reachability
    const files = await glob('**/*.{md,mdx,js,jsx,ts,tsx,html}', {
      cwd: options.directory,
      ignore: ['node_modules/**', 'dist/**', 'build/**', '.next/**', 'examples/**']
    });
    const urlRegex = /(https?:\/\/[^\s)"'<>]+)/g;
    const uniqueUrls = new Map<string, { file: string; line: number }[]>();
    for (const file of files) {
      let content = '';
      try { content = readFileSync(`${options.directory}/${file}`, 'utf-8'); } catch {}
      const lines = content.split('\n');
      lines.forEach((line, idx) => {
        let m: RegExpExecArray | null;
        const re = new RegExp(urlRegex);
        while ((m = re.exec(line)) !== null) {
          const url = m[1];
          const arr = uniqueUrls.get(url) || [];
          arr.push({ file, line: idx + 1 });
          uniqueUrls.set(url, arr);
        }
      });
    }
    const checkUrl = (url: string): Promise<number> => {
      return new Promise(resolve => {
        const lib = url.startsWith('https') ? https : http;
        const req = lib.request(url, { method: 'HEAD', timeout: 7000 }, (res) => {
          resolve(res.statusCode || 0);
          res.resume();
        });
        req.on('timeout', () => { req.destroy(new Error('timeout')); });
        req.on('error', () => resolve(0));
        req.end();
      });
    };
    const entries = Array.from(uniqueUrls.entries());
    const concurrency = 8;
    for (let i = 0; i < entries.length; i += concurrency) {
      const batch = entries.slice(i, i + concurrency);
      const statuses = await Promise.all(batch.map(([u]) => checkUrl(u)));
      for (let j = 0; j < batch.length; j++) {
        const [url, locs] = batch[j];
        const status = statuses[j];
        if (status === 0 || status >= 400) {
          const meta = RULES.LINK002;
          for (const loc of locs) {
            results.push({
              type: meta.severity === 'high' ? 'error' : 'warning',
              category: meta.category,
              message: `${meta.message}: ${url} (${status || 'timeout'})`,
              file: loc.file,
              line: loc.line,
              severity: meta.severity,
              ruleId: meta.id,
              confidence: 0.8,
              confidenceReason: status ? `HTTP ${status} response received` : 'Request timed out - URL may be unreachable',
              fix: meta.fix
            });
          }
        }
      }
    }
    if (results.length === 0) {
      // keep the informative suggestion about puppeteer for internal crawling
      const meta = RULES.LINK001;
      results.push({ type: 'info', category: meta.category, message: meta.message, ruleId: meta.id, severity: meta.severity, fix: meta.fix, confidence: 0.6, confidenceReason: 'Suggestion to add internal link checking' });
    }
    return results;
  }
}