import { PLATFORM_ID, REQUEST, RESPONSE_INIT, TransferState } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { provideHttpClient } from '@angular/common/http';
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';

import { environment } from '../../../environments/environment';
import { Tenant, TenantBootstrapResponse } from './tenant.interface';
import { TENANT_SSR_STATE_KEY } from './tenant-ssr-cache.store';
import { TenantService } from './tenant.service';

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'
};

const tenantResponse: TenantBootstrapResponse = {
  data: tenant
};

describe('TenantService', () => {
  beforeEach(() => {
    try {
      window.history.replaceState({}, '', 'http://localhost:4200/');
    } catch (e) {
      // Ignore SecurityError under test environments
    }
  });

  it('requests the tenant bootstrap endpoint using the current hostname and stores the response', async () => {
    TestBed.configureTestingModule({
      providers: [provideHttpClient(), provideHttpClientTesting(), TenantService]
    });

    const service = TestBed.inject(TenantService);
    const httpController = TestBed.inject(HttpTestingController);

    const bootstrapPromise = service.bootstrap();
    const request = httpController.expectOne(`${environment.url}tenants/bootstrap/localhost`);

    expect(request.request.method).toBe('GET');

    request.flush(tenantResponse);

    await expect(bootstrapPromise).resolves.toBeUndefined();
    expect(service.status()).toBe('ready');
    expect(service.tenant()).toEqual(tenant);
    expect(service.getTenant()).toEqual(tenant);

    httpController.verify();
  });

  it('hydrates the tenant from TransferState without performing HTTP on the browser', async () => {
    TestBed.configureTestingModule({
      providers: [provideHttpClient(), provideHttpClientTesting(), TenantService]
    });

    const service = TestBed.inject(TenantService);
    const transferState = TestBed.inject(TransferState);
    const httpController = TestBed.inject(HttpTestingController);

    transferState.set(TENANT_SSR_STATE_KEY, {
      status: 'ready',
      tenant
    });

    await expect(service.bootstrap()).resolves.toBeUndefined();
    expect(service.status()).toBe('ready');
    expect(service.tenant()).toEqual(tenant);
    expect(transferState.get(TENANT_SSR_STATE_KEY, null)).toBeNull();

    httpController.expectNone(() => true);
    httpController.verify();
  });

  it('uses the forwarded host during SSR and stores the resolved tenant in TransferState', async () => {
    const responseInit: ResponseInit = {};

    TestBed.configureTestingModule({
      providers: [
        provideHttpClient(),
        provideHttpClientTesting(),
        TenantService,
        { provide: PLATFORM_ID, useValue: 'server' },
        {
          provide: REQUEST,
          useValue: new Request('https://internal.example/render', {
            headers: {
              'x-forwarded-host': 'STORE.EXAMPLE.COM:443, proxy.internal',
              host: 'ignored.example.com'
            }
          })
        },
        { provide: RESPONSE_INIT, useValue: responseInit }
      ]
    });

    const service = TestBed.inject(TenantService);
    const transferState = TestBed.inject(TransferState);
    const httpController = TestBed.inject(HttpTestingController);

    const bootstrapPromise = service.bootstrap();
    const request = httpController.expectOne(`${environment.url}tenants/bootstrap/store.example.com`);

    request.flush(tenantResponse);

    await expect(bootstrapPromise).resolves.toBeUndefined();
    expect(service.status()).toBe('ready');
    expect(service.tenant()).toEqual(tenant);
    expect(transferState.get(TENANT_SSR_STATE_KEY, null)).toEqual({
      status: 'ready',
      tenant
    });
    expect(responseInit.status).toBeUndefined();

    httpController.verify();
  });

  it('stores a not-found state and sets the SSR response status to 404 when the tenant does not exist', async () => {
    const responseInit: ResponseInit = {};

    TestBed.configureTestingModule({
      providers: [
        provideHttpClient(),
        provideHttpClientTesting(),
        TenantService,
        { provide: PLATFORM_ID, useValue: 'server' },
        {
          provide: REQUEST,
          useValue: new Request('https://internal.example/render', {
            headers: {
              host: 'missing.example.com:8443'
            }
          })
        },
        { provide: RESPONSE_INIT, useValue: responseInit }
      ]
    });

    const service = TestBed.inject(TenantService);
    const transferState = TestBed.inject(TransferState);
    const httpController = TestBed.inject(HttpTestingController);

    const bootstrapPromise = service.bootstrap();
    const request = httpController.expectOne(`${environment.url}tenants/bootstrap/missing.example.com`);

    request.flush({ message: 'Not Found' }, { status: 404, statusText: 'Not Found' });

    await expect(bootstrapPromise).resolves.toBeUndefined();
    expect(service.status()).toBe('not-found');
    expect(service.tenant()).toBeNull();
    expect(service.getTenant()).toBeNull();
    expect(transferState.get(TENANT_SSR_STATE_KEY, null)).toEqual({
      status: 'not-found'
    });
    expect(responseInit.status).toBe(404);

    httpController.verify();
  });

  it('rejects when the bootstrap response does not contain tenant data', async () => {
    TestBed.configureTestingModule({
      providers: [provideHttpClient(), provideHttpClientTesting(), TenantService]
    });

    const service = TestBed.inject(TenantService);
    const httpController = TestBed.inject(HttpTestingController);

    const bootstrapPromise = service.bootstrap();
    const request = httpController.expectOne(`${environment.url}tenants/bootstrap/localhost`);

    request.flush({});

    await expect(bootstrapPromise).rejects.toThrow('Tenant bootstrap returned no data');
    expect(service.status()).toBe('idle');
    expect(service.tenant()).toBeNull();

    httpController.verify();
  });

  it('propagates non-404 backend failures', async () => {
    const responseInit: ResponseInit = {};

    TestBed.configureTestingModule({
      providers: [
        provideHttpClient(),
        provideHttpClientTesting(),
        TenantService,
        { provide: PLATFORM_ID, useValue: 'server' },
        {
          provide: REQUEST,
          useValue: new Request('https://internal.example/render', {
            headers: {
              host: 'broken.example.com'
            }
          })
        },
        { provide: RESPONSE_INIT, useValue: responseInit }
      ]
    });

    const service = TestBed.inject(TenantService);
    const httpController = TestBed.inject(HttpTestingController);

    const bootstrapPromise = service.bootstrap();
    const request = httpController.expectOne(`${environment.url}tenants/bootstrap/broken.example.com`);

    request.flush({ message: 'Boom' }, { status: 500, statusText: 'Server Error' });

    await expect(bootstrapPromise).rejects.toThrow('Http failure response');
    expect(service.status()).toBe('idle');
    expect(responseInit.status).toBeUndefined();

    httpController.verify();
  });
});
