import {
  BackendApiError,
  BackendErrorCode,
  BillingSummary,
  HealthCheckResponse,
  PurchaseProductId,
  RevenueCatCustomerInfo,
  RevenueCatSyncSource,
  ScanPlantResponse,
  SemanticSearchResponse,
  ServiceHealthResponse,
  SimulatedWebhookEvent,
  SimulatePurchaseResponse,
  SimulateWebhookResponse,
  SyncRevenueCatStateResponse,
} from './contracts';
import { getAuthToken } from './userIdentityService';
import { mockBackendService } from './mockBackendService';
import { CareInfo, Language } from '../../types';
import { getConfiguredBackendRootUrl } from '../../utils/backendUrl';

const REQUEST_TIMEOUT_MS = 15000;

const mapHttpStatusToErrorCode = (status: number): BackendErrorCode => {
  if (status === 400) return 'BAD_REQUEST';
  if (status === 401 || status === 403) return 'UNAUTHORIZED';
  if (status === 402) return 'INSUFFICIENT_CREDITS';
  if (status === 408 || status === 504) return 'TIMEOUT';
  return 'PROVIDER_ERROR';
};

const buildBackendUrl = (path: string): string => {
  const backendBaseUrl = getConfiguredBackendRootUrl();
  if (!backendBaseUrl) return path;
  const base = backendBaseUrl.replace(/\/$/, '');
  return `${base}${path}`;
};

const parseMaybeJson = (value: string): Record<string, unknown> | null => {
  if (!value) return null;
  try {
    return JSON.parse(value) as Record<string, unknown>;
  } catch {
    return null;
  }
};

const makeRequest = async <T,>(
  path: string,
  options: {
    method: 'GET' | 'POST';
    token: string;
    idempotencyKey?: string;
    body?: Record<string, unknown>;
  },
): Promise<T> => {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);

  try {
    const headers: Record<string, string> = {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${options.token}`,
    };
    if (options.idempotencyKey) {
      headers['Idempotency-Key'] = options.idempotencyKey;
    }

    const response = await fetch(buildBackendUrl(path), {
      method: options.method,
      headers,
      body: options.body ? JSON.stringify(options.body) : undefined,
      signal: controller.signal,
    });

    const rawText = await response.text();
    const jsonPayload = parseMaybeJson(rawText);

    if (!response.ok) {
      const payloadCode = jsonPayload?.code;
      const code = typeof payloadCode === 'string'
        ? payloadCode as BackendErrorCode
        : mapHttpStatusToErrorCode(response.status);
      const payloadMessage = jsonPayload?.message;
      const message = typeof payloadMessage === 'string'
        ? payloadMessage
        : `Backend request failed with status ${response.status}.`;
      throw new BackendApiError(code, message, response.status, jsonPayload || undefined);
    }

    if (jsonPayload == null) {
      throw new BackendApiError('PROVIDER_ERROR', 'Backend returned invalid JSON.', 502);
    }

    return jsonPayload as T;
  } catch (error) {
    if (error instanceof BackendApiError) throw error;
    if (error instanceof Error && error.name === 'AbortError') {
      throw new BackendApiError('TIMEOUT', 'Backend request timed out.', 408);
    }
    throw new BackendApiError('NETWORK_ERROR', 'No network connection.', 0, {
      detail: error instanceof Error ? error.message : String(error),
    });
  } finally {
    clearTimeout(timeout);
  }
};

export const backendApiClient = {
  getServiceHealth: async (): Promise<ServiceHealthResponse> => {
    if (!getConfiguredBackendRootUrl()) {
      return {
        ok: true,
        uptimeSec: 0,
        timestamp: new Date().toISOString(),
        openAiConfigured: Boolean(process.env.EXPO_PUBLIC_OPENAI_API_KEY),
        dbReady: true,
        dbPath: 'in-app-mock-backend',
        scanModel: (process.env.EXPO_PUBLIC_OPENAI_SCAN_MODEL || 'gpt-5').trim(),
        healthModel: (process.env.EXPO_PUBLIC_OPENAI_HEALTH_MODEL || process.env.EXPO_PUBLIC_OPENAI_SCAN_MODEL || 'gpt-5').trim(),
      };
    }

    const token = await getAuthToken();
    return makeRequest<ServiceHealthResponse>('/health', {
      method: 'GET',
      token,
    });
  },

  getBillingSummary: async (): Promise<BillingSummary> => {
    const token = await getAuthToken();
    if (!getConfiguredBackendRootUrl()) {
      return mockBackendService.getBillingSummary(token);
    }

    return makeRequest<BillingSummary>('/v1/billing/summary', {
      method: 'GET',
      token,
    });
  },

  syncRevenueCatState: async (params: {
    customerInfo: RevenueCatCustomerInfo;
    source?: RevenueCatSyncSource;
  }): Promise<SyncRevenueCatStateResponse> => {
    const token = await getAuthToken();
    if (!getConfiguredBackendRootUrl()) {
        return mockBackendService.syncRevenueCatState({
          userId: token,
          customerInfo: params.customerInfo,
          source: params.source,
        });
    }

    return makeRequest<SyncRevenueCatStateResponse>('/v1/billing/sync-revenuecat', {
      method: 'POST',
      token,
      body: {
        customerInfo: params.customerInfo,
        source: params.source,
      },
    });
  },

  scanPlant: async (params: {
    idempotencyKey: string;
    imageUri: string;
    language: Language;
  }): Promise<ScanPlantResponse> => {
    const token = await getAuthToken();
    if (!getConfiguredBackendRootUrl()) {
      return mockBackendService.scanPlant({
        userId: token,
        idempotencyKey: params.idempotencyKey,
        imageUri: params.imageUri,
        language: params.language,
      });
    }

    return makeRequest<ScanPlantResponse>('/v1/scan', {
      method: 'POST',
      token,
      idempotencyKey: params.idempotencyKey,
      body: {
        imageUri: params.imageUri,
        language: params.language,
      },
    });
  },

  semanticSearch: async (params: {
    idempotencyKey: string;
    query: string;
    language: Language;
  }): Promise<SemanticSearchResponse> => {
    const token = await getAuthToken();
    if (!getConfiguredBackendRootUrl()) {
      return mockBackendService.semanticSearch({
        userId: token,
        idempotencyKey: params.idempotencyKey,
        query: params.query,
        language: params.language,
      });
    }

    return makeRequest<SemanticSearchResponse>('/v1/search/semantic', {
      method: 'POST',
      token,
      idempotencyKey: params.idempotencyKey,
      body: {
        query: params.query,
        language: params.language,
      },
    });
  },

  runHealthCheck: async (params: {
    idempotencyKey: string;
    imageUri: string;
    language: Language;
    plantContext?: {
      name: string;
      botanicalName: string;
      careInfo: CareInfo;
      description?: string;
    };
  }): Promise<HealthCheckResponse> => {
    const token = await getAuthToken();
    if (!getConfiguredBackendRootUrl()) {
      return mockBackendService.healthCheck({
        userId: token,
        idempotencyKey: params.idempotencyKey,
        imageUri: params.imageUri,
        language: params.language,
        plantContext: params.plantContext,
      });
    }

    return makeRequest<HealthCheckResponse>('/v1/health-check', {
      method: 'POST',
      token,
      idempotencyKey: params.idempotencyKey,
      body: {
        imageUri: params.imageUri,
        language: params.language,
        plantContext: params.plantContext,
      },
    });
  },

  simulatePurchase: async (params: {
    idempotencyKey: string;
    productId: PurchaseProductId;
  }): Promise<SimulatePurchaseResponse> => {
    const token = await getAuthToken();
    if (!getConfiguredBackendRootUrl()) {
      return mockBackendService.simulatePurchase({
        userId: token,
        idempotencyKey: params.idempotencyKey,
        productId: params.productId,
      });
    }

    return makeRequest<SimulatePurchaseResponse>('/v1/billing/simulate-purchase', {
      method: 'POST',
      token,
      idempotencyKey: params.idempotencyKey,
      body: {
        productId: params.productId,
      },
    });
  },

  simulateWebhook: async (params: {
    idempotencyKey: string;
    event: SimulatedWebhookEvent;
    payload?: { credits?: number };
  }): Promise<SimulateWebhookResponse> => {
    const token = await getAuthToken();
    if (!getConfiguredBackendRootUrl()) {
      return mockBackendService.simulateWebhook({
        userId: token,
        idempotencyKey: params.idempotencyKey,
        event: params.event,
        payload: params.payload,
      });
    }

    return makeRequest<SimulateWebhookResponse>('/v1/billing/simulate-webhook', {
      method: 'POST',
      token,
      idempotencyKey: params.idempotencyKey,
      body: {
        event: params.event,
        payload: params.payload || {},
      },
    });
  },
};

export const isInsufficientCreditsError = (error: unknown): boolean => {
  return error instanceof BackendApiError && error.code === 'INSUFFICIENT_CREDITS';
};

export const isNetworkError = (error: unknown): boolean => {
  return (
    error instanceof BackendApiError &&
    (error.code === 'NETWORK_ERROR' || error.code === 'TIMEOUT')
  );
};
