/**
 * TouchBistro API Client
 * Production-grade client with auth, rate limiting, retry logic
 */

import type {
  TouchBistroConfig,
  ApiResponse,
  ListParams,
  Order,
  OrderItem,
  MenuItem,
  MenuCategory,
  Menu,
  ModifierGroup,
  Table,
  Section,
  FloorPlan,
  Staff,
  TimeSheet,
  Customer,
  LoyaltyTransaction,
  Reservation,
  InventoryItem,
  StockAdjustment,
  Supplier,
  PurchaseOrder,
  Payment,
  SalesReport,
  InventoryReport,
  StaffReport,
} from './types.js';

export class TouchBistroApiClient {
  private config: TouchBistroConfig;
  private baseUrl: string;
  private accessToken: string | null = null;
  private tokenExpiry: number = 0;
  private rateLimitRemaining: number = 1000;
  private rateLimitReset: number = 0;

  constructor(config: TouchBistroConfig) {
    this.config = config;
    this.baseUrl = config.baseUrl || 'https://cloud.touchbistro.com/api/v1';
    if (config.sandbox) {
      this.baseUrl = 'https://sandbox.touchbistro.com/api/v1';
    }
  }

  // ============================================================================
  // Authentication & Request Management
  // ============================================================================

  private async ensureAuthenticated(): Promise<void> {
    if (this.accessToken && Date.now() < this.tokenExpiry) {
      return;
    }

    const response = await fetch(`${this.baseUrl}/auth/token`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        client_id: this.config.clientId,
        client_secret: this.config.clientSecret,
        grant_type: 'client_credentials',
      }),
    });

    if (!response.ok) {
      throw new Error(`Authentication failed: ${response.statusText}`);
    }

    const data = await response.json();
    this.accessToken = data.access_token;
    this.tokenExpiry = Date.now() + (data.expires_in - 60) * 1000; // Refresh 1 min early
  }

  private async checkRateLimit(): Promise<void> {
    if (this.rateLimitRemaining <= 0 && Date.now() < this.rateLimitReset) {
      const waitTime = this.rateLimitReset - Date.now();
      await new Promise((resolve) => setTimeout(resolve, waitTime));
    }
  }

  private async request<T>(
    method: string,
    endpoint: string,
    body?: any,
    retries = 3
  ): Promise<ApiResponse<T>> {
    await this.ensureAuthenticated();
    await this.checkRateLimit();

    const url = `${this.baseUrl}${endpoint}`;
    const headers: Record<string, string> = {
      'Authorization': `Bearer ${this.accessToken}`,
      'X-Restaurant-ID': this.config.restaurantId,
      'Content-Type': 'application/json',
    };

    try {
      const response = await fetch(url, {
        method,
        headers,
        body: body ? JSON.stringify(body) : undefined,
      });

      // Update rate limit tracking
      this.rateLimitRemaining = parseInt(
        response.headers.get('X-RateLimit-Remaining') || '1000'
      );
      this.rateLimitReset = parseInt(
        response.headers.get('X-RateLimit-Reset') || '0'
      );

      if (!response.ok) {
        if (response.status === 429 && retries > 0) {
          // Rate limited, wait and retry
          await new Promise((resolve) => setTimeout(resolve, 1000));
          return this.request<T>(method, endpoint, body, retries - 1);
        }

        if (response.status >= 500 && retries > 0) {
          // Server error, retry with exponential backoff
          await new Promise((resolve) => setTimeout(resolve, (4 - retries) * 1000));
          return this.request<T>(method, endpoint, body, retries - 1);
        }

        const error = await response.json().catch(() => ({
          code: 'UNKNOWN_ERROR',
          message: response.statusText,
        }));

        return {
          success: false,
          error: {
            code: error.code || `HTTP_${response.status}`,
            message: error.message || response.statusText,
            details: error.details,
          },
        };
      }

      const data = await response.json();
      return {
        success: true,
        data,
        metadata: {
          timestamp: new Date().toISOString(),
        },
      };
    } catch (error: any) {
      if (retries > 0) {
        await new Promise((resolve) => setTimeout(resolve, 1000));
        return this.request<T>(method, endpoint, body, retries - 1);
      }

      return {
        success: false,
        error: {
          code: 'NETWORK_ERROR',
          message: error.message,
        },
      };
    }
  }

  // ============================================================================
  // Orders API
  // ============================================================================

  async getOrders(params?: ListParams): Promise<ApiResponse<Order[]>> {
    const queryParams = new URLSearchParams();
    if (params?.page) queryParams.set('page', params.page.toString());
    if (params?.pageSize) queryParams.set('pageSize', params.pageSize.toString());
    if (params?.sortBy) queryParams.set('sortBy', params.sortBy);
    if (params?.sortOrder) queryParams.set('sortOrder', params.sortOrder);
    if (params?.filter) {
      Object.entries(params.filter).forEach(([key, value]) => {
        queryParams.set(key, String(value));
      });
    }

    return this.request<Order[]>('GET', `/orders?${queryParams}`);
  }

  async getOrder(orderId: string): Promise<ApiResponse<Order>> {
    return this.request<Order>('GET', `/orders/${orderId}`);
  }

  async createOrder(order: Partial<Order>): Promise<ApiResponse<Order>> {
    return this.request<Order>('POST', '/orders', order);
  }

  async updateOrder(orderId: string, updates: Partial<Order>): Promise<ApiResponse<Order>> {
    return this.request<Order>('PATCH', `/orders/${orderId}`, updates);
  }

  async addOrderItem(orderId: string, item: Partial<OrderItem>): Promise<ApiResponse<Order>> {
    return this.request<Order>('POST', `/orders/${orderId}/items`, item);
  }

  async updateOrderItem(
    orderId: string,
    itemId: string,
    updates: Partial<OrderItem>
  ): Promise<ApiResponse<Order>> {
    return this.request<Order>('PATCH', `/orders/${orderId}/items/${itemId}`, updates);
  }

  async removeOrderItem(orderId: string, itemId: string): Promise<ApiResponse<Order>> {
    return this.request<Order>('DELETE', `/orders/${orderId}/items/${itemId}`);
  }

  async sendOrderToKitchen(orderId: string): Promise<ApiResponse<Order>> {
    return this.request<Order>('POST', `/orders/${orderId}/send-to-kitchen`);
  }

  async completeOrder(orderId: string): Promise<ApiResponse<Order>> {
    return this.request<Order>('POST', `/orders/${orderId}/complete`);
  }

  async voidOrder(orderId: string, reason: string): Promise<ApiResponse<Order>> {
    return this.request<Order>('POST', `/orders/${orderId}/void`, { reason });
  }

  async splitOrder(
    orderId: string,
    splits: { items: string[]; amount?: number }[]
  ): Promise<ApiResponse<Order[]>> {
    return this.request<Order[]>('POST', `/orders/${orderId}/split`, { splits });
  }

  // ============================================================================
  // Menu Management API
  // ============================================================================

  async getMenuItems(params?: ListParams): Promise<ApiResponse<MenuItem[]>> {
    const queryParams = new URLSearchParams();
    if (params?.filter) {
      Object.entries(params.filter).forEach(([key, value]) => {
        queryParams.set(key, String(value));
      });
    }
    return this.request<MenuItem[]>('GET', `/menu/items?${queryParams}`);
  }

  async getMenuItem(itemId: string): Promise<ApiResponse<MenuItem>> {
    return this.request<MenuItem>('GET', `/menu/items/${itemId}`);
  }

  async createMenuItem(item: Partial<MenuItem>): Promise<ApiResponse<MenuItem>> {
    return this.request<MenuItem>('POST', '/menu/items', item);
  }

  async updateMenuItem(itemId: string, updates: Partial<MenuItem>): Promise<ApiResponse<MenuItem>> {
    return this.request<MenuItem>('PATCH', `/menu/items/${itemId}`, updates);
  }

  async deleteMenuItem(itemId: string): Promise<ApiResponse<void>> {
    return this.request<void>('DELETE', `/menu/items/${itemId}`);
  }

  async getMenuCategories(): Promise<ApiResponse<MenuCategory[]>> {
    return this.request<MenuCategory[]>('GET', '/menu/categories');
  }

  async createMenuCategory(category: Partial<MenuCategory>): Promise<ApiResponse<MenuCategory>> {
    return this.request<MenuCategory>('POST', '/menu/categories', category);
  }

  async updateMenuCategory(
    categoryId: string,
    updates: Partial<MenuCategory>
  ): Promise<ApiResponse<MenuCategory>> {
    return this.request<MenuCategory>('PATCH', `/menu/categories/${categoryId}`, updates);
  }

  async deleteMenuCategory(categoryId: string): Promise<ApiResponse<void>> {
    return this.request<void>('DELETE', `/menu/categories/${categoryId}`);
  }

  async getMenus(): Promise<ApiResponse<Menu[]>> {
    return this.request<Menu[]>('GET', '/menu/menus');
  }

  async createMenu(menu: Partial<Menu>): Promise<ApiResponse<Menu>> {
    return this.request<Menu>('POST', '/menu/menus', menu);
  }

  async updateMenu(menuId: string, updates: Partial<Menu>): Promise<ApiResponse<Menu>> {
    return this.request<Menu>('PATCH', `/menu/menus/${menuId}`, updates);
  }

  async getModifierGroups(): Promise<ApiResponse<ModifierGroup[]>> {
    return this.request<ModifierGroup[]>('GET', '/menu/modifier-groups');
  }

  async createModifierGroup(group: Partial<ModifierGroup>): Promise<ApiResponse<ModifierGroup>> {
    return this.request<ModifierGroup>('POST', '/menu/modifier-groups', group);
  }

  // ============================================================================
  // Table & Floor Management API
  // ============================================================================

  async getTables(params?: ListParams): Promise<ApiResponse<Table[]>> {
    return this.request<Table[]>('GET', '/tables');
  }

  async getTable(tableId: string): Promise<ApiResponse<Table>> {
    return this.request<Table>('GET', `/tables/${tableId}`);
  }

  async updateTableStatus(
    tableId: string,
    status: string,
    guestCount?: number
  ): Promise<ApiResponse<Table>> {
    return this.request<Table>('PATCH', `/tables/${tableId}/status`, { status, guestCount });
  }

  async assignServer(tableId: string, serverId: string): Promise<ApiResponse<Table>> {
    return this.request<Table>('PATCH', `/tables/${tableId}/assign`, { serverId });
  }

  async getSections(): Promise<ApiResponse<Section[]>> {
    return this.request<Section[]>('GET', '/sections');
  }

  async createSection(section: Partial<Section>): Promise<ApiResponse<Section>> {
    return this.request<Section>('POST', '/sections', section);
  }

  async getFloorPlans(): Promise<ApiResponse<FloorPlan[]>> {
    return this.request<FloorPlan[]>('GET', '/floor-plans');
  }

  async getActiveFloorPlan(): Promise<ApiResponse<FloorPlan>> {
    return this.request<FloorPlan>('GET', '/floor-plans/active');
  }

  async createTable(table: Partial<Table>): Promise<ApiResponse<Table>> {
    return this.request<Table>('POST', '/tables', table);
  }

  async updateTable(tableId: string, updates: Partial<Table>): Promise<ApiResponse<Table>> {
    return this.request<Table>('PATCH', `/tables/${tableId}`, updates);
  }

  // ============================================================================
  // Staff Management API
  // ============================================================================

  async getStaff(params?: ListParams): Promise<ApiResponse<Staff[]>> {
    return this.request<Staff[]>('GET', '/staff');
  }

  async getStaffMember(staffId: string): Promise<ApiResponse<Staff>> {
    return this.request<Staff>('GET', `/staff/${staffId}`);
  }

  async createStaffMember(staff: Partial<Staff>): Promise<ApiResponse<Staff>> {
    return this.request<Staff>('POST', '/staff', staff);
  }

  async updateStaffMember(staffId: string, updates: Partial<Staff>): Promise<ApiResponse<Staff>> {
    return this.request<Staff>('PATCH', `/staff/${staffId}`, updates);
  }

  async deactivateStaffMember(staffId: string): Promise<ApiResponse<Staff>> {
    return this.request<Staff>('POST', `/staff/${staffId}/deactivate`);
  }

  async clockIn(staffId: string): Promise<ApiResponse<TimeSheet>> {
    return this.request<TimeSheet>('POST', `/staff/${staffId}/clock-in`);
  }

  async clockOut(staffId: string): Promise<ApiResponse<TimeSheet>> {
    return this.request<TimeSheet>('POST', `/staff/${staffId}/clock-out`);
  }

  async getTimeSheets(params?: {
    staffId?: string;
    dateFrom?: string;
    dateTo?: string;
  }): Promise<ApiResponse<TimeSheet[]>> {
    const queryParams = new URLSearchParams();
    if (params?.staffId) queryParams.set('staffId', params.staffId);
    if (params?.dateFrom) queryParams.set('dateFrom', params.dateFrom);
    if (params?.dateTo) queryParams.set('dateTo', params.dateTo);
    return this.request<TimeSheet[]>('GET', `/timesheets?${queryParams}`);
  }

  // ============================================================================
  // Customer Management API
  // ============================================================================

  async getCustomers(params?: ListParams): Promise<ApiResponse<Customer[]>> {
    const queryParams = new URLSearchParams();
    if (params?.filter?.search) {
      queryParams.set('search', params.filter.search);
    }
    return this.request<Customer[]>('GET', `/customers?${queryParams}`);
  }

  async getCustomer(customerId: string): Promise<ApiResponse<Customer>> {
    return this.request<Customer>('GET', `/customers/${customerId}`);
  }

  async createCustomer(customer: Partial<Customer>): Promise<ApiResponse<Customer>> {
    return this.request<Customer>('POST', '/customers', customer);
  }

  async updateCustomer(
    customerId: string,
    updates: Partial<Customer>
  ): Promise<ApiResponse<Customer>> {
    return this.request<Customer>('PATCH', `/customers/${customerId}`, updates);
  }

  async searchCustomers(query: string): Promise<ApiResponse<Customer[]>> {
    return this.request<Customer[]>('GET', `/customers/search?q=${encodeURIComponent(query)}`);
  }

  async getLoyaltyTransactions(customerId: string): Promise<ApiResponse<LoyaltyTransaction[]>> {
    return this.request<LoyaltyTransaction[]>('GET', `/customers/${customerId}/loyalty`);
  }

  async addLoyaltyPoints(
    customerId: string,
    points: number,
    description: string
  ): Promise<ApiResponse<LoyaltyTransaction>> {
    return this.request<LoyaltyTransaction>('POST', `/customers/${customerId}/loyalty`, {
      points,
      description,
      type: 'earned',
    });
  }

  async redeemLoyaltyPoints(
    customerId: string,
    points: number,
    description: string
  ): Promise<ApiResponse<LoyaltyTransaction>> {
    return this.request<LoyaltyTransaction>('POST', `/customers/${customerId}/loyalty`, {
      points: -points,
      description,
      type: 'redeemed',
    });
  }

  // ============================================================================
  // Reservations API
  // ============================================================================

  async getReservations(params?: {
    dateFrom?: string;
    dateTo?: string;
    status?: string;
  }): Promise<ApiResponse<Reservation[]>> {
    const queryParams = new URLSearchParams();
    if (params?.dateFrom) queryParams.set('dateFrom', params.dateFrom);
    if (params?.dateTo) queryParams.set('dateTo', params.dateTo);
    if (params?.status) queryParams.set('status', params.status);
    return this.request<Reservation[]>('GET', `/reservations?${queryParams}`);
  }

  async getReservation(reservationId: string): Promise<ApiResponse<Reservation>> {
    return this.request<Reservation>('GET', `/reservations/${reservationId}`);
  }

  async createReservation(reservation: Partial<Reservation>): Promise<ApiResponse<Reservation>> {
    return this.request<Reservation>('POST', '/reservations', reservation);
  }

  async updateReservation(
    reservationId: string,
    updates: Partial<Reservation>
  ): Promise<ApiResponse<Reservation>> {
    return this.request<Reservation>('PATCH', `/reservations/${reservationId}`, updates);
  }

  async confirmReservation(reservationId: string): Promise<ApiResponse<Reservation>> {
    return this.request<Reservation>('POST', `/reservations/${reservationId}/confirm`);
  }

  async seatReservation(reservationId: string, tableId: string): Promise<ApiResponse<Reservation>> {
    return this.request<Reservation>('POST', `/reservations/${reservationId}/seat`, { tableId });
  }

  async cancelReservation(reservationId: string, reason?: string): Promise<ApiResponse<Reservation>> {
    return this.request<Reservation>('POST', `/reservations/${reservationId}/cancel`, { reason });
  }

  async markNoShow(reservationId: string): Promise<ApiResponse<Reservation>> {
    return this.request<Reservation>('POST', `/reservations/${reservationId}/no-show`);
  }

  // ============================================================================
  // Inventory Management API
  // ============================================================================

  async getInventoryItems(params?: ListParams): Promise<ApiResponse<InventoryItem[]>> {
    return this.request<InventoryItem[]>('GET', '/inventory/items');
  }

  async getInventoryItem(itemId: string): Promise<ApiResponse<InventoryItem>> {
    return this.request<InventoryItem>('GET', `/inventory/items/${itemId}`);
  }

  async createInventoryItem(item: Partial<InventoryItem>): Promise<ApiResponse<InventoryItem>> {
    return this.request<InventoryItem>('POST', '/inventory/items', item);
  }

  async updateInventoryItem(
    itemId: string,
    updates: Partial<InventoryItem>
  ): Promise<ApiResponse<InventoryItem>> {
    return this.request<InventoryItem>('PATCH', `/inventory/items/${itemId}`, updates);
  }

  async adjustStock(
    itemId: string,
    quantity: number,
    type: 'addition' | 'subtraction' | 'correction',
    reason: string
  ): Promise<ApiResponse<StockAdjustment>> {
    return this.request<StockAdjustment>('POST', `/inventory/items/${itemId}/adjust`, {
      quantity,
      type,
      reason,
    });
  }

  async getLowStockItems(): Promise<ApiResponse<InventoryItem[]>> {
    return this.request<InventoryItem[]>('GET', '/inventory/items/low-stock');
  }

  async getSuppliers(): Promise<ApiResponse<Supplier[]>> {
    return this.request<Supplier[]>('GET', '/inventory/suppliers');
  }

  async createSupplier(supplier: Partial<Supplier>): Promise<ApiResponse<Supplier>> {
    return this.request<Supplier>('POST', '/inventory/suppliers', supplier);
  }

  async getPurchaseOrders(params?: { status?: string }): Promise<ApiResponse<PurchaseOrder[]>> {
    const queryParams = new URLSearchParams();
    if (params?.status) queryParams.set('status', params.status);
    return this.request<PurchaseOrder[]>('GET', `/inventory/purchase-orders?${queryParams}`);
  }

  async createPurchaseOrder(order: Partial<PurchaseOrder>): Promise<ApiResponse<PurchaseOrder>> {
    return this.request<PurchaseOrder>('POST', '/inventory/purchase-orders', order);
  }

  async receivePurchaseOrder(orderId: string): Promise<ApiResponse<PurchaseOrder>> {
    return this.request<PurchaseOrder>('POST', `/inventory/purchase-orders/${orderId}/receive`);
  }

  // ============================================================================
  // Payment Processing API
  // ============================================================================

  async processPayment(
    orderId: string,
    payment: Partial<Payment>
  ): Promise<ApiResponse<Payment>> {
    return this.request<Payment>('POST', `/orders/${orderId}/payments`, payment);
  }

  async refundPayment(paymentId: string, amount?: number): Promise<ApiResponse<Payment>> {
    return this.request<Payment>('POST', `/payments/${paymentId}/refund`, { amount });
  }

  async voidPayment(paymentId: string): Promise<ApiResponse<Payment>> {
    return this.request<Payment>('POST', `/payments/${paymentId}/void`);
  }

  async getPayments(orderId: string): Promise<ApiResponse<Payment[]>> {
    return this.request<Payment[]>('GET', `/orders/${orderId}/payments`);
  }

  // ============================================================================
  // Reporting & Analytics API
  // ============================================================================

  async getSalesReport(dateFrom: string, dateTo: string): Promise<ApiResponse<SalesReport>> {
    return this.request<SalesReport>(
      'GET',
      `/reports/sales?dateFrom=${dateFrom}&dateTo=${dateTo}`
    );
  }

  async getInventoryReport(): Promise<ApiResponse<InventoryReport>> {
    return this.request<InventoryReport>('GET', '/reports/inventory');
  }

  async getStaffReport(dateFrom: string, dateTo: string): Promise<ApiResponse<StaffReport>> {
    return this.request<StaffReport>(
      'GET',
      `/reports/staff?dateFrom=${dateFrom}&dateTo=${dateTo}`
    );
  }

  async getTopSellingItems(limit: number = 10): Promise<ApiResponse<any[]>> {
    return this.request<any[]>('GET', `/reports/top-items?limit=${limit}`);
  }

  async getRevenueByHour(date: string): Promise<ApiResponse<any[]>> {
    return this.request<any[]>('GET', `/reports/revenue-by-hour?date=${date}`);
  }

  async getCustomerAnalytics(): Promise<ApiResponse<any>> {
    return this.request<any>('GET', '/reports/customer-analytics');
  }
}

export default TouchBistroApiClient;
