// @ts-nocheck
declare var Promise: any;
import { Page } from '@playwright/test';
import { ROUTES } from '../fixtures/data';

const NAVIGATION_TIMEOUT = Number(process.env.E2E_NAVIGATION_TIMEOUT || '60000');
const LOGIN_SETTLE_TIMEOUT = Number(process.env.E2E_LOGIN_SETTLE_TIMEOUT || '120000');

export class AppPage {
  constructor(protected page: Page) {}

  async goto(path: string) {
    const navigate = async () => {
      return await this.page.goto(path, { waitUntil: 'commit', timeout: NAVIGATION_TIMEOUT });
    };

    try {
      await navigate();
    } catch (error) {
      const message = String(error);
      if (!message.includes('ERR_ABORTED') && !message.includes('frame was detached')) {
        throw error;
      }
      await this.page.waitForLoadState('domcontentloaded');
      if (!this.page.url().includes(path)) {
        await navigate();
      }
    }
  }

  async isAuthenticated(): Promise<boolean> {
    const url = this.page.url();
    if (!url || url === 'about:blank') return false;
    return !/\/login(?:_check)?/.test(new URL(url).pathname);
  }

  async login(username: string, password: string) {
    if (await this.isAuthenticated()) {
      return;
    }

    await this.page.goto(ROUTES.login);

    await this.page.waitForSelector('input[name="_username"], input[name="username"]', {
      timeout: NAVIGATION_TIMEOUT,
    });

    const usernameSelector =
      (await this.page.locator('input[name="_username"]').first().isVisible().catch(() => false))
        ? 'input[name="_username"]'
        : 'input[name="username"]';

    const passwordSelector =
      (await this.page.locator('input[name="_password"]').first().isVisible().catch(() => false))
        ? 'input[name="_password"]'
        : 'input[name="password"]';

    await this.page.fill(usernameSelector, username);
    await this.page.fill(passwordSelector, password);

    if ((await this.page.locator('button[type="submit"]').count()) > 0) {
      await Promise.all([
        this.page.waitForNavigation({ waitUntil: 'load', timeout: NAVIGATION_TIMEOUT }).catch(() => null),
        this.page.click('button[type="submit"]'),
      ]);
    } else {
      await Promise.all([
        this.page.waitForNavigation({ waitUntil: 'load', timeout: NAVIGATION_TIMEOUT }).catch(() => null),
        this.page.locator('form').first().press('Enter'),
      ]);
    }

    await this.page
      .waitForFunction(
        () => !/\/login(?:_check)?$/.test(globalThis.location.pathname),
        null,
        { timeout: LOGIN_SETTLE_TIMEOUT }
      );

    const currentPath = new URL(this.page.url()).pathname;
    if (/\/login(?:_check)?$/.test(currentPath)) {
      throw new Error(
        'Autenticacion fallida: la aplicacion permanece en login. Verificar usuario/contrasena o estado del backend.'
      );
    }
  }

  async logout() {
    await this.page.click('a:has-text("Salir")');
    await this.page.waitForNavigation();
  }

  async isLoggedIn(): Promise<boolean> {
    const user = await this.page.$('span:has-text("Usuario")');
    return !!user;
  }

  async getStatusCode(): Promise<number> {
    const response = await this.page.goto(this.page.url());
    return response ? response.status() : 0;
  }
}

export class FormHelper {
  constructor(protected page: Page) {}

  async fillText(selector: string, value: string) {
    await this.page.fill(selector, value);
  }

  async checkCheckbox(selector: string) {
    await this.page.check(selector);
  }

  async uncheckCheckbox(selector: string) {
    await this.page.uncheck(selector);
  }

  async isCheckboxChecked(selector: string): Promise<boolean> {
    return await this.page.isChecked(selector);
  }

  async submit(buttonSelector: string = 'button[type="submit"]') {
    await this.page.click(buttonSelector);
  }

  async getInputValue(selector: string): Promise<string | null> {
    return await this.page.$eval(selector, (el: any) => el.value);
  }
}

export class AjaxHelper {
  constructor(protected page: Page) {}

  async interceptAjaxResponse(method: string, urlPattern: string) {
    return await this.page.waitForResponse(
      response =>
        response.request().method() === method &&
        response.url().includes(urlPattern)
    );
  }

  async getLastAjaxJson(urlPattern: string) {
    const response = await this.page.waitForResponse(
      response => response.url().includes(urlPattern)
    );
    return await response.json();
  }
}

// ── Monitor de errores AJAX/red ────────────────────────────────────────────
export interface NetworkError {
  status: number;
  url: string;
  method: string;
  type: 'ajax' | 'asset' | 'navigation';
  body?: string;
}

/**
 * Activa un monitor de red en la página que recolecta todos los errores HTTP
 * (status >= 400) durante la prueba. Retorna una función que devuelve los
 * errores recolectados hasta ese momento.
 *
 * Uso en un test:
 *   const getErrors = await startNetworkMonitor(page);
 *   await page.goto('...');
 *   const errors = getErrors();
 *   expect(errors, 'Errores AJAX:\n' + JSON.stringify(errors, null, 2)).toHaveLength(0);
 */
export async function startNetworkMonitor(
  page: Page,
  options: { ignorePatterns?: RegExp[]; onlyAjax?: boolean } = {}
): Promise<() => NetworkError[]> {
  const errors: NetworkError[] = [];

  const ignore = options.ignorePatterns ?? [];
  // Extensiones que no son llamadas de negocio (assets estáticos con errores tolerables)
  const ASSET_EXTS = /\.(ico|woff2?|ttf|eot|map)(\?.*)?$/i;

  page.on('response', async (response) => {
    const status = response.status();
    if (status < 400) return;

    const url = response.url();
    if (ignore.some((p) => p.test(url))) return;

    const method = response.request().method();
    const resourceType = response.request().resourceType();

    // Determinar tipo
    let type: NetworkError['type'] = 'navigation';
    if (['xhr', 'fetch'].includes(resourceType)) {
      type = 'ajax';
    } else if (['stylesheet', 'script', 'image', 'font', 'media'].includes(resourceType)) {
      if (ASSET_EXTS.test(url)) return; // ignorar assets menores
      type = 'asset';
    }

    if (options.onlyAjax && type !== 'ajax') return;

    // Intentar capturar body de la respuesta de error para contexto
    let body: string | undefined;
    try {
      const raw = await response.text();
      body = raw.length > 500 ? raw.slice(0, 500) + '…' : raw;
    } catch {
      // sin body disponible
    }

    errors.push({ status, url, method, type, body });
  });

  return () => [...errors];
}
