import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { map, catchError, switchMap } from 'rxjs/operators';
import { TpeDevice, TpeStatus, TpeType } from '../interfaces/tpe';
import { Agent, AgentStatus } from '../interfaces/agent';
import { environment } from 'src/environments/environment.development';
import { normalizePage } from '@shared/paging/normalize-page';
import { ListParams, PagedResult } from '@shared/paging/paging';

const USE_SERVER = true;
const API_BASE = '/api/v1/tpes';

// Interface to match the API response structure for Agent (nested in TPE)
interface AgentApiResponse {
  id: number;
  code: string;
  profile: string;
  principalCode?: string;
  caisseProfile?: string;
  statut: string;
  zone?: string;
  kiosk?: string;
  fonction?: string;
  dateEmbauche?: string;
  nom: string;
  prenom: string;
  autresNoms?: string;
  dateNaissance?: string;
  lieuNaissance?: string;
  ville?: string;
  adresse?: string;
  autoriserAides?: boolean;
  phone: string;
  pin?: string;
  limiteInferieure?: number;
  limiteSuperieure?: number;
  limiteParTransaction?: number;
  limiteMinAirtime?: number;
  limiteMaxAirtime?: number;
  maxPeripheriques?: number;
  limitId?: number;
  nationalite?: string;
  cni?: string;
  cniDelivreeLe?: string;
  cniDelivreeA?: string;
  residence?: string;
  autreAdresse1?: string;
  statutMarital?: string;
  epoux?: string;
  autreTelephone?: string;
  createdAt?: string;
  updatedAt?: string;
  createdBy?: string;
}

// Interface to match the API response structure
interface TpeApiResponse {
  id: number;
  imei: string;
  serial: string;
  type: string;
  marque: string;
  modele: string;
  statut: string; // API uses uppercase: VALIDE, INVALIDE, EN_PANNE, BLOQUE
  agent?: AgentApiResponse;
  assigne: boolean;
  createdAt?: string;
  updatedAt?: string;
}

// Stats interfaces
interface CountByStatutResponse {
  [key: string]: number;
}

// Assignment stats is just a number (count of assigned TPEs)
type AssignesStatsResponse = number;

@Injectable({ providedIn: 'root' })
export class TpeService {
  private apiUrl = environment.apiBaseUrl + API_BASE;

  constructor(private http: HttpClient) {}

  // Helper method to get ngrok bypass headers
  private getNgrokHeaders(): Record<string, string> {
    const isNgrok =
      environment.apiBaseUrl.includes('ngrok-free.app') ||
      environment.apiBaseUrl.includes('ngrok.io') ||
      environment.apiBaseUrl.includes('ngrok');
    return isNgrok ? { 'ngrok-skip-browser-warning': 'true' } : {};
  }

  // Transform API statut to interface statut (both use uppercase now)
  private transformStatut(apiStatut: string): TpeStatus {
    const upperStatut = apiStatut.toUpperCase() as TpeStatus;
    const validStatuses: TpeStatus[] = [
      'VALIDE',
      'INVALIDE',
      'EN_PANNE',
      'BLOQUE',
      'DISPONIBLE',
      'AFFECTE',
      'EN_MAINTENANCE',
      'HORS_SERVICE',
      'VOLE',
    ];
    return validStatuses.includes(upperStatut) ? upperStatut : 'INVALIDE';
  }

  // Transform interface statut to API statut (both use uppercase now, so direct return)
  private transformStatutToApi(statut: TpeStatus): string {
    return statut; // Already uppercase, no transformation needed
  }

  // Transform API Agent response to Agent
  private transformAgent(apiAgent: AgentApiResponse): Agent {
    return {
      id: String(apiAgent.id),
      code: apiAgent.code,
      profile: apiAgent.profile,
      principalCode: apiAgent.principalCode,
      caisseProfile: apiAgent.caisseProfile,
      statut: apiAgent.statut as AgentStatus,
      zone: apiAgent.zone,
      kiosk: apiAgent.kiosk,
      fonction: apiAgent.fonction,
      dateEmbauche: apiAgent.dateEmbauche,
      nom: apiAgent.nom,
      prenom: apiAgent.prenom,
      autresNoms: apiAgent.autresNoms,
      dateNaissance: apiAgent.dateNaissance,
      lieuNaissance: apiAgent.lieuNaissance,
      ville: apiAgent.ville,
      adresse: apiAgent.adresse,
      autoriserAides: apiAgent.autoriserAides,
      phone: apiAgent.phone,
      pin: apiAgent.pin,
      limiteInferieure: apiAgent.limiteInferieure,
      limiteSuperieure: apiAgent.limiteSuperieure,
      limiteParTransaction: apiAgent.limiteParTransaction,
      limiteMinAirtime: apiAgent.limiteMinAirtime,
      limiteMaxAirtime: apiAgent.limiteMaxAirtime,
      maxPeripheriques: apiAgent.maxPeripheriques,
      limitId: apiAgent.limitId ? String(apiAgent.limitId) : undefined,
      nationalite: apiAgent.nationalite,
      cni: apiAgent.cni,
      cniDelivreeLe: apiAgent.cniDelivreeLe,
      cniDelivreeA: apiAgent.cniDelivreeA,
      residence: apiAgent.residence,
      autreAdresse1: apiAgent.autreAdresse1,
      statutMarital: apiAgent.statutMarital,
      epoux: apiAgent.epoux,
      autreTelephone: apiAgent.autreTelephone,
      createdAt: apiAgent.createdAt,
      updatedAt: apiAgent.updatedAt,
      createdBy: apiAgent.createdBy,
    };
  }

  // Transform API response to TpeDevice
  private transformTpe(apiTpe: TpeApiResponse): TpeDevice {
    return {
      id: String(apiTpe.id),
      imei: apiTpe.imei,
      serial: apiTpe.serial,
      type: apiTpe.type as TpeType,
      marque: apiTpe.marque,
      modele: apiTpe.modele,
      statut: this.transformStatut(apiTpe.statut),
      agent: apiTpe.agent ? this.transformAgent(apiTpe.agent) : undefined,
      assigne: apiTpe.assigne,
      createdAt: apiTpe.createdAt,
      updatedAt: apiTpe.updatedAt,
    };
  }

  // Transform TpeDevice to API payload
  private transformToApiPayload(tpe: Partial<TpeDevice>): any {
    const payload: any = {};
    if (tpe.imei !== undefined) payload.imei = tpe.imei;
    if (tpe.serial !== undefined) payload.serial = tpe.serial;
    if (tpe.type !== undefined) payload.type = tpe.type;
    if (tpe.marque !== undefined) payload.marque = tpe.marque;
    if (tpe.modele !== undefined) payload.modele = tpe.modele;
    if (tpe.statut !== undefined) payload.statut = this.transformStatutToApi(tpe.statut);
    if (tpe.assigne !== undefined) payload.assigne = tpe.assigne;
    return payload;
  }

  // GET /api/v1/tpes/{id} - Get by ID
  getById(id: string): Observable<TpeDevice | undefined> {
    if (USE_SERVER) {
      return this.http
        .get<TpeApiResponse>(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() })
        .pipe(
          map((apiTpe) => this.transformTpe(apiTpe)),
          catchError((err) => {
            console.error(`Error fetching TPE ${id}:`, err);
            return of(undefined);
          })
        );
    }
    return of(undefined);
  }

  // GET /api/v1/tpes - List all
  list(params?: ListParams): Observable<PagedResult<TpeDevice>> {
    if (USE_SERVER) {
      let httpParams = new HttpParams();
      if (params) {
        if (params.page) httpParams = httpParams.set('page', params.page.toString());
        if (params.perPage) httpParams = httpParams.set('perPage', params.perPage.toString());
        if (params.search) httpParams = httpParams.set('search', params.search);
        if (params.sortKey) httpParams = httpParams.set('sortKey', params.sortKey);
        if (params.sortDir) httpParams = httpParams.set('sortDir', params.sortDir);
      }

      return this.http
        .get<TpeApiResponse[]>(this.apiUrl, {
          params: httpParams,
          headers: this.getNgrokHeaders(),
        })
        .pipe(
          map((list) => {
            const tpes = list.map((apiTpe) => this.transformTpe(apiTpe));
            // If pagination params provided, return paginated result
            if (params) {
              return normalizePage<TpeDevice>(
                { data: tpes, meta: { total: tpes.length } },
                params.page || 1,
                params.perPage || 10
              );
            }
            // Otherwise return all as single page
            return normalizePage<TpeDevice>(
              { data: tpes, meta: { total: tpes.length } },
              1,
              tpes.length
            );
          }),
          catchError((err) => {
            console.error('Error fetching TPEs:', err);
            return of(normalizePage<TpeDevice>({ data: [], meta: { total: 0 } }, 1, 10));
          })
        );
    }
    return of(normalizePage<TpeDevice>({ data: [], meta: { total: 0 } }, 1, 10));
  }

  // POST /api/v1/tpes - Create
  create(payload: Omit<TpeDevice, 'id' | 'createdAt' | 'updatedAt'>): Observable<TpeDevice> {
    if (USE_SERVER) {
      const apiPayload = this.transformToApiPayload(payload);
      return this.http
        .post<TpeApiResponse>(this.apiUrl, apiPayload, { headers: this.getNgrokHeaders() })
        .pipe(
          map((apiTpe) => this.transformTpe(apiTpe)),
          catchError((err) => {
            console.error('Error creating TPE:', err);
            throw err;
          })
        );
    }
    throw new Error('Server mode is required');
  }

  // PUT /api/v1/tpes/{id} - Update
  update(id: string, payload: Partial<TpeDevice>): Observable<TpeDevice | undefined> {
    if (USE_SERVER) {
      const apiPayload = this.transformToApiPayload(payload);
      return this.http
        .put<TpeApiResponse>(`${this.apiUrl}/${id}`, apiPayload, {
          headers: this.getNgrokHeaders(),
        })
        .pipe(
          map((apiTpe) => this.transformTpe(apiTpe)),
          catchError((err) => {
            console.error(`Error updating TPE ${id}:`, err);
            return of(undefined);
          })
        );
    }
    return of(undefined);
  }

  // DELETE /api/v1/tpes/{id} - Delete
  delete(id: string): Observable<boolean> {
    if (USE_SERVER) {
      return this.http
        .delete<void>(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() })
        .pipe(
          map(() => true),
          catchError((err) => {
            console.error(`Error deleting TPE ${id}:`, err);
            return of(false);
          })
        );
    }
    return of(false);
  }

  // PATCH /api/v1/tpes/{id}/statut - Update statut
  updateStatut(id: string, statut: TpeStatus): Observable<TpeDevice | undefined> {
    if (USE_SERVER) {
      return this.http
        .patch<TpeApiResponse>(
          `${this.apiUrl}/${id}/statut`,
          { statut: this.transformStatutToApi(statut) },
          { headers: this.getNgrokHeaders() }
        )
        .pipe(
          map((apiTpe) => this.transformTpe(apiTpe)),
          catchError((err) => {
            console.error(`Error updating TPE statut ${id}:`, err);
            return of(undefined);
          })
        );
    }
    return of(undefined);
  }

  // PATCH /api/v1/tpes/{id}/liberer - Liberate TPE (updates whole TPE, sets assigne to false and statut to DISPONIBLE)
  liberer(id: string): Observable<TpeDevice | undefined> {
    if (USE_SERVER) {
      // First get the current TPE data
      return this.getById(id).pipe(
        switchMap((tpe) => {
          if (!tpe) {
            return of(undefined);
          }
          // Update the whole TPE with assigne set to false and statut to DISPONIBLE
          const updatedTpe = { ...tpe, assigne: false, statut: 'DISPONIBLE' as TpeStatus };
          const apiPayload = this.transformToApiPayload(updatedTpe);
          return this.http
            .patch<TpeApiResponse>(`${this.apiUrl}/liberer/${id}`, apiPayload, {
              headers: this.getNgrokHeaders(),
            })
            .pipe(
              map((apiTpe) => this.transformTpe(apiTpe)),
              catchError((err) => {
                console.error(`Error liberating TPE ${id}:`, err);
                return of(undefined);
              })
            );
        }),
        catchError((err) => {
          console.error(`Error fetching TPE ${id} for liberation:`, err);
          return of(undefined);
        })
      );
    }
    return of(undefined);
  }

  // PATCH /api/v1/tpes/assigner - Assign TPE
  // Payload: { tpeId: number, agentId: number }
  assigner(id: string, agentId: string): Observable<TpeDevice | undefined> {
    if (USE_SERVER) {
      const payload = {
        tpeId: Number(id),
        agentId: Number(agentId),
      };
      return this.http
        .patch<TpeApiResponse>(`${this.apiUrl}/assigner`, payload, {
          headers: this.getNgrokHeaders(),
        })
        .pipe(
          map((apiTpe) => this.transformTpe(apiTpe)),
          catchError((err) => {
            console.error(`Error assigning TPE ${id}:`, err);
            return of(undefined);
          })
        );
    }
    return of(undefined);
  }

  // GET /api/v1/tpes/statut/{statut} - List by statut
  getByStatut(statut: TpeStatus): Observable<TpeDevice[]> {
    if (USE_SERVER) {
      const apiStatut = this.transformStatutToApi(statut);
      return this.http
        .get<TpeApiResponse[]>(`${this.apiUrl}/statut/${apiStatut}`, {
          headers: this.getNgrokHeaders(),
        })
        .pipe(
          map((list) => list.map((apiTpe) => this.transformTpe(apiTpe))),
          catchError((err) => {
            console.error(`Error fetching TPEs by statut ${statut}:`, err);
            return of([]);
          })
        );
    }
    return of([]);
  }

  // GET /api/v1/tpes/stats/count-by-statut - Get count by statut
  getCountByStatut(): Observable<CountByStatutResponse> {
    if (USE_SERVER) {
      return this.http
        .get<CountByStatutResponse>(`${this.apiUrl}/stats/count-by-statut`, {
          headers: this.getNgrokHeaders(),
        })
        .pipe(
          catchError((err) => {
            console.error('Error fetching TPE count by statut:', err);
            return of({});
          })
        );
    }
    return of({});
  }

  // GET /api/v1/tpes/stats/assignes - Get assignment stats (returns a number)
  getAssignesStats(): Observable<number> {
    if (USE_SERVER) {
      return this.http
        .get<number>(`${this.apiUrl}/stats/assignes`, {
          headers: this.getNgrokHeaders(),
        })
        .pipe(
          catchError((err) => {
            console.error('Error fetching TPE assignment stats:', err);
            return of(0);
          })
        );
    }
    return of(0);
  }

  // GET /api/v1/tpes/search - Search
  search(query: string): Observable<TpeDevice[]> {
    if (USE_SERVER) {
      return this.http
        .get<TpeApiResponse[]>(`${this.apiUrl}/search`, {
          params: { q: query.trim() },
          headers: this.getNgrokHeaders(),
        })
        .pipe(
          map((list) => list.map((apiTpe) => this.transformTpe(apiTpe))),
          catchError((err) => {
            console.error(`Error searching TPEs with query ${query}:`, err);
            return of([]);
          })
        );
    }
    return of([]);
  }

  // GET /api/v1/tpes/disponibles - List available TPEs
  getDisponibles(): Observable<TpeDevice[]> {
    if (USE_SERVER) {
      return this.http
        .get<TpeApiResponse[]>(`${this.apiUrl}/disponibles`, { headers: this.getNgrokHeaders() })
        .pipe(
          map((list) => list.map((apiTpe) => this.transformTpe(apiTpe))),
          catchError((err) => {
            console.error('Error fetching available TPEs:', err);
            return of([]);
          })
        );
    }
    return of([]);
  }
}
