import { computed, signal } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { provideRouter, Router } from '@angular/router';

import { of } from 'rxjs';

import { App } from './app';
import { AuthService } from './core/services/auth/auth.service';
import { CartService } from './core/services/cart/cart.service';
import { CheckoutService } from './core/services/checkout.service';
import { Tenant } from './core/services/tenant.interface';
import { TenantService } from './core/services/tenant.service';
import { routes } from './app.routes';

const tenant: Tenant = {
  id: 1,
  codigo: 'test',
  nombre: 'Test Tenant',
  dominio: 'localhost',
  primary_color: '#6376F3',
  secondary_color: '#A0A0A0',
  danger_color: '#FF8888',
  success_color: '#198754',
  header_bg_color: '#313131',
  footer_bg_color: '#313131',
  header_logo: 'https://example.com/header.png',
  footer_logo: 'https://example.com/footer.png',
  categories: []
};

function createTenantServiceStub(
  status: 'ready' | 'not-found' = 'ready',
  currentTenant: Tenant | null = tenant
) {
  const tenantState = signal(currentTenant);
  const statusState = signal(status);

  return {
    tenant: tenantState.asReadonly(),
    status: statusState.asReadonly(),
    getTenant: () => tenantState(),
    getTenantApiUrl: () => {
      const tenantVal = tenantState();
      if (!tenantVal) {
        throw new Error('No se pudo resolver el tenant activo.');
      }
      return `http://localhost:8000/api/tenants/${tenantVal.codigo}`;
    },
    bootstrap: vi.fn().mockResolvedValue(undefined)
  };
}

function createAuthServiceStub(isAuthenticated = false) {
  const tokenState = signal<string | null>(isAuthenticated ? 'test-token' : null);
  const userState = signal(
    isAuthenticated ? { id: 1, nombre_apellido: 'Ada Lovelace', email: 'ada@example.com' } : null
  );

  return {
    user: userState.asReadonly(),
    token: tokenState.asReadonly(),
    isAuthenticated: computed(() => tokenState() !== null),
    login: vi.fn(),
    register: vi.fn(),
    logout: vi.fn(),
    loadCurrentUser: vi.fn(),
    hydrateSession: vi.fn(),
    bootstrap: vi.fn().mockResolvedValue(undefined),
    clearSession: vi.fn(() => {
      userState.set(null);
      tokenState.set(null);
    })
  };
}

async function renderAppAt(
  url: string,
  tenantService: ReturnType<typeof createTenantServiceStub> = createTenantServiceStub(),
  authService: ReturnType<typeof createAuthServiceStub> = createAuthServiceStub()
) {
  TestBed.resetTestingModule();

  await TestBed.configureTestingModule({
    imports: [App],
    providers: [
      provideRouter(routes),
      {
        provide: TenantService,
        useValue: tenantService
      },
      {
        provide: CartService,
        useValue: {
          cart: signal(null).asReadonly(),
          loadCart: () => of({ id: 1, items: [], subtotal: '0' })
        }
      },
      {
        provide: AuthService,
        useValue: authService
      },
      {
        provide: CheckoutService,
        useValue: {
          withCustomLoading() {
            return this;
          },
          getPurchase: vi.fn().mockResolvedValue({
            id: 25,
            status: 'created',
            items: [],
            subtotal: '0.00',
            total: '0.00'
          })
        }
      }
    ]
  }).compileComponents();

  const router = TestBed.inject(Router);
  const fixture = TestBed.createComponent(App);

  fixture.detectChanges();
  await router.navigateByUrl(url);
  await fixture.whenStable();
  fixture.detectChanges();

  return { fixture, router };
}

describe('app routes', () => {
  it('loads the store layout at /', async () => {
    const { fixture, router } = await renderAppAt('/');
    const compiled = fixture.nativeElement as HTMLElement;

    expect(router.url).toBe('/');
    expect(compiled.querySelector('.store-layout')).not.toBeNull();
    expect(compiled.querySelector('app-store-home-page')).not.toBeNull();
    expect(compiled.textContent).toContain('Productos');
    expect(compiled.style.getPropertyValue('--tenant-primary')).toBe(tenant.primary_color);
  });

  it('loads componentes-test under a valid tenant', async () => {
    const { fixture, router } = await renderAppAt('/');
    await router.navigateByUrl('/componentes-test/reutilizables');
    await fixture.whenStable();
    fixture.detectChanges();

    const compiled = fixture.nativeElement as HTMLElement;

    expect(router.url).toBe('/componentes-test/reutilizables');
    expect(compiled.textContent).toContain('Componentes reutilizables');
  });

  it('loads the login page at /login and shows the create account CTA', async () => {
    const { fixture, router } = await renderAppAt('/login');
    const compiled = fixture.nativeElement as HTMLElement;

    expect(router.url).toBe('/login');
    expect(compiled.querySelector('app-login-page')).not.toBeNull();
    expect(compiled.textContent).toContain('Iniciar sesion');
    expect(compiled.textContent).toContain('Crear cuenta');
  });

  it('loads the register page at /register and renders the shared auth fields', async () => {
    const { fixture, router } = await renderAppAt('/register');
    const compiled = fixture.nativeElement as HTMLElement;
    const registerPage = compiled.querySelector('app-register-page');
    const placeholders = Array.from(registerPage?.querySelectorAll('input') ?? []).map((input) =>
      input.getAttribute('placeholder')
    );

    expect(router.url).toBe('/register');
    expect(registerPage).not.toBeNull();
    expect(compiled.textContent).toContain('CREAR CUENTA');
    expect(compiled.textContent).toContain('Volver');
    expect(placeholders).toEqual([
      'Nombre y Apellido',
      'Email',
      'Contraseña',
      'Repetir Contraseña'
    ]);
  });

  it('loads the recover password page inside the simple layout', async () => {
    const { fixture, router } = await renderAppAt('/recuperar-contrasena');
    const compiled = fixture.nativeElement as HTMLElement;
    const recoverPasswordPage = compiled.querySelector('app-recover-password-page');

    expect(router.url).toBe('/recuperar-contrasena');
    expect(compiled.querySelector('.simple-layout')).not.toBeNull();
    expect(recoverPasswordPage).not.toBeNull();
    expect(recoverPasswordPage?.textContent).toContain('Recuperar contraseña');
    expect(recoverPasswordPage?.querySelector('input')?.getAttribute('placeholder')).toBe('Email');
    expect(recoverPasswordPage?.textContent).toContain('Recuperar acceso');
    expect(recoverPasswordPage?.textContent).toContain('Volver');
  });

  it('loads the recovery code page and shows the entered email', async () => {
    const { fixture, router } = await renderAppAt(
      '/recuperar-contrasena/codigo?email=ada%40example.com'
    );
    const compiled = fixture.nativeElement as HTMLElement;
    const codePage = compiled.querySelector('app-recover-password-code-page');

    expect(router.url).toBe('/recuperar-contrasena/codigo?email=ada%40example.com');
    expect(compiled.querySelector('.simple-layout')).not.toBeNull();
    expect(codePage).not.toBeNull();
    expect(codePage?.textContent).toContain('ada@example.com');
    expect(codePage?.querySelectorAll('app-input')).toHaveLength(4);
    expect(codePage?.textContent).toContain('Validar');
  });

  it('loads the reset password page with the shared password inputs', async () => {
    const { fixture, router } = await renderAppAt(
      '/recuperar-contrasena/restablecer?email=ada%40example.com&code=1234'
    );
    const compiled = fixture.nativeElement as HTMLElement;
    const resetPage = compiled.querySelector('app-reset-password-page');
    const placeholders = Array.from(resetPage?.querySelectorAll('input') ?? []).map((input) =>
      input.getAttribute('placeholder')
    );

    expect(router.url).toBe(
      '/recuperar-contrasena/restablecer?email=ada%40example.com&code=1234'
    );
    expect(compiled.querySelector('.simple-layout')).not.toBeNull();
    expect(resetPage).not.toBeNull();
    expect(resetPage?.textContent).toContain('Restablecer contraseña');
    expect(placeholders).toEqual(['Nueva Contraseña', 'Repetir Nueva Contraseña']);
    expect(resetPage?.textContent).toContain('Guardar');
  });

  it('redirects authenticated users away from /login', async () => {
    const { router } = await renderAppAt('/login', createTenantServiceStub(), createAuthServiceStub(true));

    expect(router.url).toBe('/');
  });

  it('renders the tenant not found screen for any route when the tenant is missing', async () => {
    const { fixture } = await renderAppAt(
      '/componentes-test/reutilizables',
      createTenantServiceStub('not-found', null),
      createAuthServiceStub()
    );
    const compiled = fixture.nativeElement as HTMLElement;

    expect(compiled.querySelector('.tenant-status')).not.toBeNull();
    expect(compiled.textContent).toContain('No encontramos una tienda para este dominio');
  });

  it('renders the tenant not found screen at / without redirecting to itself', async () => {
    const { fixture, router } = await renderAppAt(
      '/',
      createTenantServiceStub('not-found', null),
      createAuthServiceStub()
    );
    const compiled = fixture.nativeElement as HTMLElement;

    expect(router.url).toBe('/');
    expect(compiled.querySelector('.tenant-status')).not.toBeNull();
    expect(compiled.textContent).toContain('No encontramos una tienda para este dominio');
  });

  it('redirects unauthenticated users from /checkout/:id to /login', async () => {
    const { router } = await renderAppAt('/checkout/25', createTenantServiceStub(), createAuthServiceStub(false));

    expect(router.url).toBe('/login?returnUrl=%2Fcheckout%2F25');
  });

  it('allows authenticated users to access /checkout/:id', async () => {
    const checkoutTenant = {
      ...tenant,
      menues: [
        {
          id: 1,
          code: 'checkout',
          label: 'Checkout',
          parent_menu_code: null,
          content_type: 'dynamic' as const,
          route: '/checkout',
          submenues: []
        }
      ]
    };
    const { router } = await renderAppAt(
      '/checkout/25',
      createTenantServiceStub('ready', checkoutTenant),
      createAuthServiceStub(true)
    );

    expect(router.url).toBe('/checkout/25');
  });
});
