import { describe, test, beforeAll, afterAll, expect } from 'vitest';
import { getWebsiteRouteContracts, RouteContract } from '../../shared/website/RouteContractSpec';
import { WebsiteServerHarness } from '../harness/WebsiteServerHarness';
import { ApiServerHarness } from '../harness/ApiServerHarness';
import { HttpDiagnostics } from '../../shared/website/HttpDiagnostics';

const WEBSITE_BASE_URL = process.env.WEBSITE_BASE_URL || 'http://localhost:3005';
const API_BASE_URL = process.env.API_BASE_URL || 'http://localhost:3006';

// Ensure WebsiteRouteManager uses the same persistence mode as the API harness
process.env.GRIDPILOT_API_PERSISTENCE = 'inmemory';

describe('Website SSR Integration', () => {
  let websiteHarness: WebsiteServerHarness | null = null;
  let apiHarness: ApiServerHarness | null = null;
  const contracts = getWebsiteRouteContracts();

  beforeAll(async () => {
    // 1. Start API
    console.log(`[WebsiteSSR] Starting API harness on ${API_BASE_URL}...`);
    apiHarness = new ApiServerHarness({
      port: parseInt(new URL(API_BASE_URL).port) || 3006,
    });
    await apiHarness.start();
    console.log(`[WebsiteSSR] API Harness started.`);

    // 2. Start Website
    console.log(`[WebsiteSSR] Starting website harness on ${WEBSITE_BASE_URL}...`);
    websiteHarness = new WebsiteServerHarness({
      port: parseInt(new URL(WEBSITE_BASE_URL).port) || 3005,
      env: {
        PORT: '3005',
        API_BASE_URL: API_BASE_URL,
        NEXT_PUBLIC_API_BASE_URL: API_BASE_URL,
        NODE_ENV: 'test',
      },
    });
    await websiteHarness.start();
    console.log(`[WebsiteSSR] Website Harness started.`);
  }, 180000);

  afterAll(async () => {
    if (websiteHarness) {
      await websiteHarness.stop();
    }
    if (apiHarness) {
      await apiHarness.stop();
    }
  });

  test.each(contracts)('SSR for $path ($accessLevel)', async (contract: RouteContract) => {
    const url = `${WEBSITE_BASE_URL}${contract.path}`;
    
    const response = await fetch(url, {
      method: 'GET',
      redirect: 'manual',
    });

    const status = response.status;
    const location = response.headers.get('location');
    const html = await response.text();

    const failureContext = {
      url,
      status,
      location,
      html: html.substring(0, 1000), // Limit HTML in logs
      serverLogs: websiteHarness?.getLogTail(60),
    };

    const formatFailure = (extra: string) => HttpDiagnostics.formatHttpFailure({ ...failureContext, extra });

    // 1. Assert Status
    if (contract.expectedStatus === 'ok') {
      if (status !== 200) {
        throw new Error(formatFailure(`Expected status 200 OK, but got ${status}`));
      }
    } else if (contract.expectedStatus === 'redirect') {
      if (status !== 302 && status !== 307) {
        throw new Error(formatFailure(`Expected redirect status (302/307), but got ${status}`));
      }
      
      // 2. Assert Redirect Location
      if (contract.expectedRedirectTo) {
        if (!location) {
          throw new Error(formatFailure(`Expected redirect to ${contract.expectedRedirectTo}, but got no Location header`));
        }
        const locationPathname = new URL(location, WEBSITE_BASE_URL).pathname;
        if (locationPathname !== contract.expectedRedirectTo) {
          throw new Error(formatFailure(`Expected redirect to pathname "${contract.expectedRedirectTo}", but got "${locationPathname}" (full: ${location})`));
        }
      }
    } else if (contract.expectedStatus === 'notFoundAllowed') {
      if (status !== 404 && status !== 200) {
        throw new Error(formatFailure(`Expected 404 or 200 (notFoundAllowed), but got ${status}`));
      }
    } else if (contract.expectedStatus === 'errorRoute') {
      // Error routes themselves should return 200 or their respective error codes (like 500)
      if (status >= 600) {
        throw new Error(formatFailure(`Error route returned unexpected status ${status}`));
      }
    }

    // 3. Assert SSR HTML Markers (only if not a redirect)
    if (status === 200 || status === 404) {
      if (contract.ssrMustContain) {
        for (const marker of contract.ssrMustContain) {
          if (typeof marker === 'string') {
            if (!html.includes(marker)) {
              throw new Error(formatFailure(`SSR HTML missing expected marker: "${marker}"`));
            }
          } else if (marker instanceof RegExp) {
            if (!marker.test(html)) {
              throw new Error(formatFailure(`SSR HTML missing expected regex marker: ${marker}`));
            }
          }
        }
      }

      if (contract.ssrMustNotContain) {
        for (const marker of contract.ssrMustNotContain) {
          if (typeof marker === 'string') {
            if (html.includes(marker)) {
              throw new Error(formatFailure(`SSR HTML contains forbidden marker: "${marker}"`));
            }
          } else if (marker instanceof RegExp) {
            if (marker.test(html)) {
              throw new Error(formatFailure(`SSR HTML contains forbidden regex marker: ${marker}`));
            }
          }
        }
      }
      
      if (contract.minTextLength && html.length < contract.minTextLength) {
          throw new Error(formatFailure(`SSR HTML length ${html.length} is less than minimum ${contract.minTextLength}`));
      }
    }
  }, 30000);
});
