import { isPlatformBrowser, isPlatformServer } from '@angular/common';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import {
  inject,
  Injectable,
  PLATFORM_ID,
  REQUEST,
  RESPONSE_INIT,
  signal,
  TransferState
} from '@angular/core';
import { IS_DISCOVERING_ROUTES } from '@angular/ssr';
import { firstValueFrom } from 'rxjs';

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

@Injectable({
  providedIn: 'root'
})
export class TenantService {
  private readonly http = inject(HttpClient);
  private readonly platformId = inject(PLATFORM_ID);
  private readonly request = inject(REQUEST, { optional: true });
  private readonly responseInit = inject(RESPONSE_INIT, { optional: true });
  private readonly transferState = inject(TransferState);
  private readonly isDiscoveringRoutes = inject(IS_DISCOVERING_ROUTES, { optional: true }) ?? false;
  private readonly tenantState = signal<Tenant | null>(null);
  private readonly statusState = signal<TenantBootstrapStatus>('idle');

  readonly tenant = this.tenantState.asReadonly();
  readonly status = this.statusState.asReadonly();

  getTenant(): Tenant | null {
    return this.tenantState();
  }

  getTenantApiUrl(): string {
    const tenant = this.tenantState();
    if (!tenant) {
      throw new Error('No se pudo resolver el tenant activo.');
    }
    return `${environment.url}tenants/${tenant.codigo}`;
  }


  async bootstrap(): Promise<void> {
    if (this.statusState() !== 'idle') {
      return;
    }

    if (this.isDiscoveringRoutes) {
      this.statusState.set('ready');

      return;
    }

    const transferredState = this.transferState.get(TENANT_SSR_STATE_KEY, null);

    if (transferredState) {
      this.transferState.remove(TENANT_SSR_STATE_KEY);
      this.applyState(transferredState);

      return;
    }

    const domain = this.resolveDomain();

    try {
      const response = await firstValueFrom(
        this.http.get<TenantBootstrapResponse>(`${environment.url}tenants/bootstrap/${domain}`)
      );

      if (!response?.data) {
        throw new Error(`Tenant bootstrap returned no data for domain "${domain}".`);
      }

      this.setReady(response.data);
      this.persistSsrState({
        status: 'ready',
        tenant: response.data
      });
    } catch (error) {
      if (this.isNotFoundError(error)) {
        this.setNotFound();
        this.persistSsrState({ status: 'not-found' });

        if (this.responseInit) {
          this.responseInit.status = 404;
        }

        return;
      }

      throw error;
    }
  }

  private resolveDomain(): string {
    if (isPlatformBrowser(this.platformId)) {
      const domain = window.location.hostname;

      if (!domain) {
        throw new Error('Tenant bootstrap could not resolve the current domain.');
      }

      return this.normalizeHost(domain);
    }

    const requestHost =
      this.request?.headers.get('x-forwarded-host') ??
      this.request?.headers.get('host') ??
      (this.request ? new URL(this.request.url).host : null);

    if (!requestHost) {
      throw new Error('Tenant bootstrap could not resolve the current domain.');
    }

    return this.normalizeHost(requestHost);
  }

  private normalizeHost(host: string): string {
    const [firstHost = ''] = host.split(',');
    const normalizedHost = firstHost.trim().toLowerCase();

    if (!normalizedHost) {
      throw new Error('Tenant bootstrap could not resolve the current domain.');
    }

    if (normalizedHost.startsWith('[')) {
      const closingBracketIndex = normalizedHost.indexOf(']');

      if (closingBracketIndex >= 0) {
        return normalizedHost.slice(1, closingBracketIndex);
      }
    }

    return normalizedHost.replace(/:\d+$/, '');
  }

  private applyState(state: TenantSsrState): void {
    if (state.status === 'ready') {
      this.setReady(state.tenant);

      return;
    }

    this.setNotFound();
  }

  private setReady(tenant: Tenant): void {
    this.tenantState.set(tenant);
    this.statusState.set('ready');
  }

  private setNotFound(): void {
    this.tenantState.set(null);
    this.statusState.set('not-found');
  }

  private persistSsrState(state: TenantSsrState): void {
    if (isPlatformServer(this.platformId)) {
      this.transferState.set(TENANT_SSR_STATE_KEY, state);
    }
  }

  private isNotFoundError(error: unknown): error is HttpErrorResponse {
    return error instanceof HttpErrorResponse && error.status === 404;
  }
}
