import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, BehaviorSubject, of, throwError } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
import { environment } from '@environments/environment';

export interface Machine {
  id?: number;
  machine_id?: string;
  machine_model: string;
  machine_type: string;
  client_id: number;
  client_name?: string;
  branch_id?: string;
  branch_name: string;
  operation_status: string;
  connection_status: string;
  created_on?: string;
  password?: string;
  slots?: any[];
  last_maintenance_date?: string;
  next_maintenance_date?: string;
  firmware_version?: string;
  
  // Created by fields (automatic from backend)
  created_by?: number;
  created_by_username?: string;
  
  // ⭐ Option 3: Assigned Refillers
  assigned_refillers?: AssignedRefiller[];
  
  // UI helper for password toggle
  showPassword?: boolean;
}

export interface AssignedRefiller {
  id: number;
  username: string;
  email: string;
  assigned_at: string;
}

export interface Refiller {
  id: number;
  user_id: string;
  username: string;
  email: string;
  roles: string;
  assigned_to?: number;
  assigned_to_username?: string;
}

export interface VendingStateResponse {
  vendingRows: any[];
  lastUpdated?: string;
}

@Injectable({
  providedIn: 'root'
})
export class MachineService {
  private apiUrl = `${environment.apiUrl}/machines`;
  private usersUrl = `${environment.apiUrl}/users`;
  private backendBaseUrl = environment.apiUrl;

  // 🔹 Loading state
  private loadingSource = new BehaviorSubject<boolean>(false);
  loading$ = this.loadingSource.asObservable();

  // 🔹 Selected machine shared state
  private selectedMachineSource = new BehaviorSubject<Machine | null>(null);
  selectedMachine$ = this.selectedMachineSource.asObservable();

  constructor(private http: HttpClient) {}

  // =============================================
  // SHARED STATE METHODS
  // =============================================
  
  setSelectedMachine(machine: Machine): void {
    this.selectedMachineSource.next(machine);
  }

  clearSelectedMachine(): void {
    this.selectedMachineSource.next(null);
  }

  // =============================================
  // BASIC MACHINE CRUD OPERATIONS
  // =============================================
  
  getMachines(): Observable<Machine[]> {
    this.loadingSource.next(true);
    return this.http.get<Machine[]>(this.apiUrl).pipe(
      tap((machines) => console.log('Fetched all machines:', machines)),
      catchError(this.handleError<Machine[]>('getMachines', [])),
      tap(() => this.loadingSource.next(false))
    );
  }

  getMachineById(id: number): Observable<Machine> {
    return this.http.get<Machine>(`${this.apiUrl}/${id}`).pipe(
      tap((m) => console.log('Fetched machine by id:', m)),
      catchError(this.handleError<Machine>('getMachineById', {} as Machine))
    );
  }

  addMachine(machine: Machine): Observable<Machine> {
    this.loadingSource.next(true);
    
    // Don't send created_by in the request body
    // Backend automatically extracts it from JWT token
    const payload = { ...machine };
    delete payload.created_by;
    delete payload.created_by_username;
    delete payload.assigned_refillers;
    delete payload.showPassword;
    
    return this.http.post<Machine>(this.apiUrl, payload).pipe(
      tap((newMachine) => console.log('Added new machine:', newMachine)),
      catchError((error) => {
        console.error('Error adding machine:', error);
        this.loadingSource.next(false);
        return throwError(() => error);
      }),
      tap(() => this.loadingSource.next(false))
    );
  }

  updateMachine(id: number, machine: Machine): Observable<Machine> {
    this.loadingSource.next(true);
    
    // Don't send created_by or assigned_refillers in updates
    const payload = { ...machine };
    delete payload.created_by;
    delete payload.created_by_username;
    delete payload.assigned_refillers;
    delete payload.showPassword;
    
    return this.http.put<Machine>(`${this.apiUrl}/${id}`, payload).pipe(
      tap((updated) => console.log('Updated machine:', updated)),
      catchError(this.handleError<Machine>('updateMachine', {} as Machine)),
      tap(() => this.loadingSource.next(false))
    );
  }

  deleteMachine(id: number): Observable<any> {
    this.loadingSource.next(true);
    return this.http.delete(`${this.apiUrl}/${id}`).pipe(
      tap(() => console.log('Deleted machine with ID:', id)),
      catchError(this.handleError<any>('deleteMachine')),
      tap(() => this.loadingSource.next(false))
    );
  }

  // =============================================
  // USER RELATED (for dropdowns)
  // =============================================
  
  getUsers(): Observable<any[]> {
    return this.http.get<any[]>(this.usersUrl).pipe(
      tap((users) => console.log('Fetched users:', users)),
      catchError(this.handleError<any[]>('getUsers', []))
    );
  }

  // =============================================
  // OPTION 3: MACHINE REFILLER ASSIGNMENTS
  // =============================================

  /**
   * Get all Refillers assigned to a Machine
   * @param machineId - The machine_id (e.g., "M001")
   */
  getMachineRefillers(machineId: string): Observable<Refiller[]> {
    return this.http.get<Refiller[]>(`${this.apiUrl}/${machineId}/refillers`).pipe(
      tap((refillers) => console.log(`Fetched refillers for machine ${machineId}:`, refillers)),
      catchError(this.handleError<Refiller[]>('getMachineRefillers', []))
    );
  }

  /**
   * Assign Refillers to a Machine
   * @param machineId - The machine_id (e.g., "M001")
   * @param refillerIds - Array of Refiller user IDs
   */
  assignRefillersToMachine(machineId: string, refillerIds: number[]): Observable<any> {
    return this.http.post(`${this.apiUrl}/${machineId}/refillers`, { refiller_ids: refillerIds }).pipe(
      tap((response) => console.log(`Assigned refillers to machine ${machineId}:`, response)),
      catchError(this.handleError<any>('assignRefillersToMachine'))
    );
  }

  /**
   * Get all available Refillers that can be assigned to a Machine
   * Filters by Refillers assigned to the Machine's Client
   */
  getAvailableRefillers(clientId?: number): Observable<Refiller[]> {
    const url = clientId 
      ? `${this.usersUrl}?roles=Refiller&assigned_to=${clientId}`
      : `${this.usersUrl}?roles=Refiller`;
    
    return this.http.get<Refiller[]>(url).pipe(
      tap((refillers) => console.log('Fetched available refillers:', refillers)),
      catchError(this.handleError<Refiller[]>('getAvailableRefillers', []))
    );
  }

  // =============================================
  // MACHINE STATUS OPERATIONS
  // =============================================
  
  updateMachineStatus(
    id: number,
    status: { operation_status?: string; connection_status?: string }
  ): Observable<Machine> {
    return this.http.patch<Machine>(`${this.apiUrl}/${id}/status`, status).pipe(
      tap((m) => console.log('Updated status:', m)),
      catchError(this.handleError<Machine>('updateMachineStatus', {} as Machine))
    );
  }

  // =============================================
  // VENDING STATE OPERATIONS
  // =============================================
  
  getMachineVendingState(machineId: string): Observable<VendingStateResponse> {
    return this.http
      .get<VendingStateResponse>(`${this.apiUrl}/${machineId}/vending-state`)
      .pipe(
        tap((state) => console.log('Fetched vending state:', state)),
        catchError(
          this.handleError<VendingStateResponse>('getMachineVendingState', {
            vendingRows: []
          })
        )
      );
  }

  updateMachineVendingState(machineId: string, vendingState: any[]): Observable<any> {
    return this.http
      .put(`${this.apiUrl}/${machineId}/vending-state`, { vendingRows: vendingState })
      .pipe(
        tap((resp) => console.log('Updated vending state:', resp)),
        catchError(this.handleError<any>('updateMachineVendingState'))
      );
  }

  // =============================================
  // MAINTENANCE OPERATIONS
  // =============================================
  
  getMachineMaintenanceHistory(machineId: string): Observable<any[]> {
    return this.http
      .get<any[]>(`${this.apiUrl}/${machineId}/maintenance-history`)
      .pipe(
        tap((h) => console.log('Fetched maintenance history:', h)),
        catchError(this.handleError<any[]>('getMachineMaintenanceHistory', []))
      );
  }

  scheduleMaintenance(machineId: string, data: any): Observable<any> {
    return this.http.post(`${this.apiUrl}/${machineId}/maintenance`, data).pipe(
      tap((r) => console.log('Scheduled maintenance:', r)),
      catchError(this.handleError<any>('scheduleMaintenance'))
    );
  }

  // =============================================
  // ERROR HANDLER
  // =============================================
  
  private handleError<T>(operation = 'operation', result?: T) {
    return (error: HttpErrorResponse): Observable<T> => {
      console.error(`${operation} failed:`, error.message);
      if (error.error instanceof ErrorEvent) {
        console.error('Client-side error:', error.error.message);
      } else {
        console.error(`Backend returned code ${error.status}, body:`, error.error);
      }
      return of(result as T);
    };
  }
}