/**
 * Plant Identification provider — the live production provider for PixieSprout.
 *
 * The file and persisted provider value retain the legacy `xiaomi-mimo` name for
 * backward compatibility. Requests currently go through the Pixie AI proxy and
 * use qwen3.5:397b; no Xiaomi credential is involved.
 */
import { runAiCall } from '../orchestration/aiQueue';
import {
  createSafeCredentialDebug,
  fetchWithTimeout,
  isAbortError,
  logDev,
  readResponseText,
  truncateForLog,
  warnDev,
} from '../providerUtils';
import {
  alignInputWithOptimizedImages,
  optimizePlantScanImages,
} from './imageOptimization';
import { getPrimaryPlantScanImageUri } from './inputNormalization';
import type { PlantIdentificationProvider } from './plantIdentificationProvider';
import { PlantIdentificationError } from './types';
import { createXiaomiMimoPrompt } from './xiaomiMimoPrompt';
import {
  mapXiaomiMimoResponse,
  readXiaomiFinishReason,
  type XiaomiChatResponse,
} from './xiaomiMimoResponse';

declare const process: {
  env: {
    EXPO_PUBLIC_PROXY_URL?: string;
    EXPO_PUBLIC_APP_TOKEN?: string;
  };
};

const defaultProxyEndpoint = 'https://pixiesprout-ai.46-4-121-190.sslip.io/v1/chat/completions';
const xiaomiModel = 'qwen3.5:397b';
const requestTimeoutMs = 120_000;

function isRecord(value: unknown): value is Record<string, unknown> {
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
}

function getProxyEndpoint(): string {
  const configuredEndpoint = (process.env.EXPO_PUBLIC_PROXY_URL || '').trim();
  return configuredEndpoint || defaultProxyEndpoint;
}

function getProxyToken(): { apiKey: string; source: string } {
  return {
    apiKey: (process.env.EXPO_PUBLIC_APP_TOKEN || '').trim(),
    source: 'EXPO_PUBLIC_APP_TOKEN',
  };
}

export const xiaomiMimoProvider: PlantIdentificationProvider = {
  name: 'xiaomi-mimo',
  async identifyPlant(input) {
    if (!getPrimaryPlantScanImageUri(input)) {
      throw new PlantIdentificationError('NO_IMAGE', 'A plant photo is required.');
    }

    const endpoint = getProxyEndpoint();
    const { apiKey, source } = getProxyToken();
    logDev('[PlantIdentification] Pixie Intelligence configuration', {
      envSource: source,
      endpoint,
      model: xiaomiModel,
      ...createSafeCredentialDebug(apiKey),
    });

    if (!apiKey) {
      warnDev('[PlantIdentification] request blocked', {
        provider: 'xiaomi-mimo',
        reason: 'missing_api_key',
      });
      throw new PlantIdentificationError('MISSING_API_KEY', 'Pixie API key is missing.');
    }

    const optimizedImages = await optimizePlantScanImages(input, 'xiaomi-mimo');
    if (optimizedImages.length === 0) {
      throw new PlantIdentificationError('NO_IMAGE', 'The selected photo could not be read.');
    }

    const requestInput = alignInputWithOptimizedImages(input, optimizedImages);

    const safeRequestDebug = {
      provider: 'xiaomi-mimo',
      endpoint,
      model: xiaomiModel,
      method: 'POST',
      contentType: 'application/json',
      imageCount: optimizedImages.length,
      imageBase64Lengths: optimizedImages.map(image => image.base64.length),
      ...createSafeCredentialDebug(apiKey),
    };

    let response: Response;
    try {
      logDev('[PlantIdentification] request started', safeRequestDebug);

      // Start the timeout inside the queued callback. Queue wait time should not
      // consume the model's full request budget.
      response = await runAiCall(
        () => fetchWithTimeout(
          endpoint,
          {
            method: 'POST',
            headers: {
              Authorization: `Bearer ${apiKey}`,
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({
              model: xiaomiModel,
              messages: [{
                role: 'user',
                content: [
                  { type: 'text', text: createXiaomiMimoPrompt(requestInput) },
                  ...optimizedImages.map(image => ({
                    type: 'image_url',
                    image_url: {
                      url: `data:image/jpeg;base64,${image.base64}`,
                    },
                  })),
                ],
              }],
              temperature: 0,
              max_tokens: 8_000,
              response_format: { type: 'json_object' },
            }),
          },
          requestTimeoutMs,
        ),
        'high',
      );

      logDev('[PlantIdentification] response received', {
        provider: 'xiaomi-mimo',
        status: response.status,
        ok: response.ok,
      });
    } catch (error) {
      const timedOut = isAbortError(error);
      warnDev('[PlantIdentification] request failed', {
        provider: 'xiaomi-mimo',
        message: timedOut ? 'timeout' : 'network_failure',
      });
      throw new PlantIdentificationError(
        'NETWORK_FAILURE',
        timedOut ? 'Pixie took too long to respond.' : 'Pixie could not be reached.',
      );
    }

    if (!response.ok) {
      const errorBody = await readResponseText(response);
      const truncatedErrorBody = truncateForLog(errorBody || 'no response body');

      warnDev('[PlantIdentification] request failed', {
        ...safeRequestDebug,
        status: response.status,
        message: response.status === 401 || response.status === 403
          ? 'auth_failure'
          : 'provider_unavailable',
        responseBody: truncatedErrorBody,
      });

      if (response.status === 401 || response.status === 403) {
        throw new PlantIdentificationError(
          'AUTH_FAILURE',
          'Pixie rejected the API key.',
          response.status,
          `Auth failed: ${response.status}. Key detected: ${apiKey ? 'yes' : 'no'}. Provider: Pixie Intelligence.`,
        );
      }

      throw new PlantIdentificationError(
        'PROVIDER_UNAVAILABLE',
        'Pixie is unavailable right now.',
        response.status,
        `Pixie Intelligence HTTP ${response.status}.`,
      );
    }

    try {
      const payload: unknown = await response.json();
      if (!isRecord(payload)) throw new Error('Response was not a JSON object.');

      const parsedResponse = payload as XiaomiChatResponse;
      if (readXiaomiFinishReason(parsedResponse) === 'length') {
        warnDev('[PlantIdentification] response truncated', {
          provider: 'xiaomi-mimo',
          finishReason: 'length',
        });
        throw new PlantIdentificationError(
          'PROVIDER_UNAVAILABLE',
          'Pixie ran out of room identifying this plant. Please try again.',
          undefined,
          'Pixie response was truncated (finish_reason=length).',
        );
      }

      return mapXiaomiMimoResponse(requestInput, parsedResponse);
    } catch (error) {
      if (error instanceof PlantIdentificationError) throw error;

      warnDev('[PlantIdentification] response parse failed', {
        provider: 'xiaomi-mimo',
        message: error instanceof Error ? error.message : 'unexpected_response',
      });
      throw new PlantIdentificationError(
        'PROVIDER_UNAVAILABLE',
        'Pixie returned an unexpected response.',
        undefined,
        'Pixie response could not be parsed by the app.',
      );
    }
  },
};
