/**
 * NocoDB Client - Clean abstraction layer for NocoDB REST API
 * Hides query syntax complexity behind simple TypeScript functions
 */

interface NocoDBConfig {
  baseUrl: string;
  baseId: string;
  apiKey: string;
  ordersTableId: string;
}

interface OrderFilters {
  status?: string;
  vesselName?: string;
  imoNumber?: string;
  dateFrom?: string;
  dateTo?: string;
  minAmount?: number;
  maxAmount?: number;
}

interface PaginationParams {
  limit?: number;
  offset?: number;
  sortBy?: string;
  sortOrder?: 'asc' | 'desc';
}

interface NocoDBResponse<T> {
  list: T[];
  pageInfo: {
    totalRows: number;
    page: number;
    pageSize: number;
    isFirstPage: boolean;
    isLastPage: boolean;
  };
}

interface OrderStats {
  totalOrders: number;
  totalRevenue: number;
  totalCO2Offset: number;
  fulfillmentRate: number;
  ordersByStatus: {
    pending: number;
    paid: number;
    fulfilled: number;
    cancelled: number;
  };
}

export class NocoDBClient {
  private config: NocoDBConfig;
  private baseUrl: string;

  constructor() {
    this.config = {
      baseUrl: process.env.NOCODB_BASE_URL || '',
      baseId: process.env.NOCODB_BASE_ID || '',
      apiKey: process.env.NOCODB_API_KEY || '',
      ordersTableId: process.env.NOCODB_ORDERS_TABLE_ID || '',
    };

    if (!this.config.baseUrl || !this.config.baseId || !this.config.apiKey || !this.config.ordersTableId) {
      console.warn('NocoDB configuration incomplete. Some features may not work.');
    }

    this.baseUrl = `${this.config.baseUrl}/api/v2/tables/${this.config.ordersTableId}/records`;
  }

  /**
   * Build NocoDB where clause from filters
   */
  private buildWhereClause(filters: OrderFilters): string {
    const conditions: string[] = [];

    if (filters.status) {
      conditions.push(`(status,eq,${filters.status})`);
    }

    if (filters.vesselName) {
      conditions.push(`(vesselName,like,%${filters.vesselName}%)`);
    }

    if (filters.imoNumber) {
      conditions.push(`(imoNumber,eq,${filters.imoNumber})`);
    }

    if (filters.dateFrom) {
      conditions.push(`(CreatedAt,gte,${filters.dateFrom})`);
    }

    if (filters.dateTo) {
      conditions.push(`(CreatedAt,lte,${filters.dateTo})`);
    }

    if (filters.minAmount !== undefined) {
      conditions.push(`(totalAmount,gte,${filters.minAmount})`);
    }

    if (filters.maxAmount !== undefined) {
      conditions.push(`(totalAmount,lte,${filters.maxAmount})`);
    }

    return conditions.length > 0 ? conditions.join('~and') : '';
  }

  /**
   * Build sort parameter
   */
  private buildSortParam(sortBy?: string, sortOrder?: 'asc' | 'desc'): string {
    if (!sortBy) return '-CreatedAt'; // Default: newest first
    const prefix = sortOrder === 'asc' ? '' : '-';
    return `${prefix}${sortBy}`;
  }

  /**
   * Make authenticated request to NocoDB
   */
  private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
    const url = endpoint.startsWith('http') ? endpoint : `${this.baseUrl}${endpoint}`;

    const response = await fetch(url, {
      ...options,
      headers: {
        'xc-token': this.config.apiKey,
        'Content-Type': 'application/json',
        ...options.headers,
      },
    });

    if (!response.ok) {
      const errorText = await response.text();
      throw new Error(`NocoDB request failed: ${response.status} - ${errorText}`);
    }

    return response.json();
  }

  /**
   * Get list of orders with filtering, sorting, and pagination
   */
  async getOrders(
    filters: OrderFilters = {},
    pagination: PaginationParams = {}
  ): Promise<NocoDBResponse<any>> {
    const params = new URLSearchParams();

    // Add where clause if filters exist
    const whereClause = this.buildWhereClause(filters);
    if (whereClause) {
      params.append('where', whereClause);
    }

    // Add sorting
    const sort = this.buildSortParam(pagination.sortBy, pagination.sortOrder);
    params.append('sort', sort);

    // Add pagination
    if (pagination.limit) {
      params.append('limit', pagination.limit.toString());
    }
    if (pagination.offset) {
      params.append('offset', pagination.offset.toString());
    }

    const queryString = params.toString();
    const endpoint = queryString ? `?${queryString}` : '';

    return this.request<NocoDBResponse<any>>(endpoint);
  }

  /**
   * Get single order by ID
   */
  async getOrderById(recordId: string): Promise<any> {
    return this.request<any>(`/${recordId}`);
  }

  /**
   * Search orders by text (searches vessel name, IMO, order ID)
   */
  async searchOrders(
    searchTerm: string,
    pagination: PaginationParams = {}
  ): Promise<NocoDBResponse<any>> {
    // Search in multiple fields using OR conditions
    const searchConditions = [
      `(vesselName,like,%${searchTerm}%)`,
      `(imoNumber,like,%${searchTerm}%)`,
      `(orderId,like,%${searchTerm}%)`,
    ].join('~or');

    const params = new URLSearchParams();
    params.append('where', searchConditions);

    const sort = this.buildSortParam(pagination.sortBy, pagination.sortOrder);
    params.append('sort', sort);

    if (pagination.limit) {
      params.append('limit', pagination.limit.toString());
    }
    if (pagination.offset) {
      params.append('offset', pagination.offset.toString());
    }

    return this.request<NocoDBResponse<any>>(`?${params.toString()}`);
  }

  /**
   * Get order statistics for dashboard
   */
  async getStats(dateFrom?: string, dateTo?: string): Promise<OrderStats> {
    const filters: OrderFilters = {};
    if (dateFrom) filters.dateFrom = dateFrom;
    if (dateTo) filters.dateTo = dateTo;

    // Get all orders with filters (no pagination for stats calculation)
    const response = await this.getOrders(filters, { limit: 10000 });
    const orders = response.list;

    // Calculate stats
    const totalOrders = orders.length;
    const totalRevenue = orders.reduce((sum, order) => sum + (order.totalAmount || 0), 0);
    const totalCO2Offset = orders.reduce((sum, order) => sum + (order.co2Tons || 0), 0);

    const ordersByStatus = {
      pending: orders.filter((o) => o.status === 'pending').length,
      paid: orders.filter((o) => o.status === 'paid').length,
      fulfilled: orders.filter((o) => o.status === 'fulfilled').length,
      cancelled: orders.filter((o) => o.status === 'cancelled').length,
    };

    const fulfillmentRate =
      totalOrders > 0 ? (ordersByStatus.fulfilled / totalOrders) * 100 : 0;

    return {
      totalOrders,
      totalRevenue,
      totalCO2Offset,
      fulfillmentRate,
      ordersByStatus,
    };
  }

  /**
   * Update order status
   */
  async updateOrderStatus(recordId: string, status: string): Promise<any> {
    return this.request<any>(`/${recordId}`, {
      method: 'PATCH',
      body: JSON.stringify({ status }),
    });
  }

  /**
   * Update order fields
   */
  async updateOrder(recordId: string, data: Record<string, any>): Promise<any> {
    return this.request<any>(`/${recordId}`, {
      method: 'PATCH',
      body: JSON.stringify(data),
    });
  }

  /**
   * Get orders grouped by time period (for charts)
   */
  async getOrdersTimeline(
    period: 'day' | 'week' | 'month',
    dateFrom?: string,
    dateTo?: string
  ): Promise<Array<{ date: string; count: number; revenue: number }>> {
    const filters: OrderFilters = {};
    if (dateFrom) filters.dateFrom = dateFrom;
    if (dateTo) filters.dateTo = dateTo;

    const response = await this.getOrders(filters, { limit: 10000 });
    const orders = response.list;

    // Group orders by time period
    const grouped = new Map<string, { count: number; revenue: number }>();

    orders.forEach((order) => {
      const date = new Date(order.CreatedAt);
      let key: string;

      switch (period) {
        case 'day':
          key = date.toISOString().split('T')[0];
          break;
        case 'week':
          const weekStart = new Date(date);
          weekStart.setDate(date.getDate() - date.getDay());
          key = weekStart.toISOString().split('T')[0];
          break;
        case 'month':
          key = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
          break;
      }

      const existing = grouped.get(key) || { count: 0, revenue: 0 };
      grouped.set(key, {
        count: existing.count + 1,
        revenue: existing.revenue + (order.totalAmount || 0),
      });
    });

    return Array.from(grouped.entries())
      .map(([date, data]) => ({ date, ...data }))
      .sort((a, b) => a.date.localeCompare(b.date));
  }

  /**
   * Get count of records matching filters
   */
  async getCount(filters: OrderFilters = {}): Promise<number> {
    const whereClause = this.buildWhereClause(filters);
    const params = whereClause ? `?where=${whereClause}` : '';

    const countUrl = `${this.config.baseUrl}/api/v2/tables/${this.config.ordersTableId}/records/count${params}`;

    const response = await this.request<{ count: number }>(countUrl);
    return response.count;
  }
}

// Export singleton instance
export const nocodbClient = new NocoDBClient();

// Export types for use in other files
export type { OrderFilters, PaginationParams, NocoDBResponse, OrderStats };
