import type {
  BulkStringItem,
  BulkTranslateResponse,
  GeminiTranslationResponse,
  Tone,
} from '../types';
import { geminiTranslationProvider } from './geminiTranslationProvider';
import { MockTranslationProvider } from './mockTranslationProvider';
import type { MockTranslationStore } from './mockTranslationStore';

export interface StructuredTranslationFields {
  title?: string;
  excerpt?: string;
  content?: string;
}

export interface TranslationProviderContext {
  jobId?: string;
  scenario?:
    | 'success'
    | 'pending_hold'
    | 'processing_hold'
    | 'rate_limited'
    | 'transient_failure'
    | 'permanent_failure'
    | 'timeout'
    | 'malformed_response';
  attempt?: number;
  signal?: AbortSignal;
  abortSignal?: AbortSignal;
  store?: MockTranslationStore;
}

export interface StructuredTranslationResponse {
  translated_title?: string;
  translated_excerpt?: string;
  translated_content?: string;
  tokens_used: number;
  input_tokens: number;
  output_tokens: number;
  processing_time_ms: number;
  model_used: string;
}

export type TranslationProviderTextResult = GeminiTranslationResponse;
export type TranslationProviderBulkResult = BulkTranslateResponse;
export type TranslationProviderStructuredResult = StructuredTranslationResponse;

export interface TranslationProvider {
  translate(
    content: string,
    sourceLang: string,
    targetLang: string,
    tone?: Tone,
    context?: TranslationProviderContext
  ): Promise<TranslationProviderTextResult>;
  translateBulk(
    strings: BulkStringItem[],
    sourceLang: string,
    targetLang: string,
    tone?: Tone,
    context?: TranslationProviderContext
  ): Promise<TranslationProviderBulkResult>;
  translateStructured(
    fields: StructuredTranslationFields,
    sourceLang: string,
    targetLang: string,
    tone?: Tone,
    context?: TranslationProviderContext
  ): Promise<TranslationProviderStructuredResult>;
}

export type TranslationProviderErrorKind =
  | 'rate_limited'
  | 'transient'
  | 'permanent'
  | 'timeout'
  | 'malformed_response';

interface DefaultProviderError {
  code: string;
  message: string;
  retryable: boolean;
}

const DEFAULT_PROVIDER_ERRORS: Record<TranslationProviderErrorKind, DefaultProviderError> = {
  rate_limited: {
    code: 'TRANSLATION_PROVIDER_RATE_LIMITED',
    message: 'Translation provider rate limit exceeded. Please try again later.',
    retryable: true,
  },
  transient: {
    code: 'TRANSLATION_PROVIDER_UNAVAILABLE',
    message: 'Translation provider is temporarily unavailable. Please try again.',
    retryable: true,
  },
  permanent: {
    code: 'TRANSLATION_PROVIDER_FAILED',
    message: 'Translation provider could not complete the translation.',
    retryable: false,
  },
  timeout: {
    code: 'TRANSLATION_PROVIDER_TIMEOUT',
    message: 'Translation provider timed out. Please try again.',
    retryable: true,
  },
  malformed_response: {
    code: 'TRANSLATION_PROVIDER_MALFORMED_RESPONSE',
    message: 'Translation provider returned an invalid response.',
    retryable: false,
  },
};

export interface TranslationProviderErrorOptions {
  kind: TranslationProviderErrorKind;
  code?: string;
  message?: string;
  retryable?: boolean;
  cause?: unknown;
}

export class TranslationProviderError extends Error {
  readonly kind: TranslationProviderErrorKind;
  readonly retryable: boolean;
  readonly code: string;
  readonly safeCode: string;
  readonly safeMessage: string;
  readonly cause?: unknown;

  constructor(options: TranslationProviderErrorOptions);
  constructor(
    kind: TranslationProviderErrorKind,
    code: string,
    message: string,
    retryable: boolean,
    cause?: unknown
  );
  constructor(
    optionsOrKind: TranslationProviderErrorOptions | TranslationProviderErrorKind,
    code?: string,
    message?: string,
    retryable?: boolean,
    cause?: unknown
  ) {
    const options: TranslationProviderErrorOptions = typeof optionsOrKind === 'string'
      ? { kind: optionsOrKind, code, message, retryable, cause }
      : optionsOrKind;
    const defaults = DEFAULT_PROVIDER_ERRORS[options.kind];
    const safeCode = options.code || defaults.code;
    const safeMessage = options.message || defaults.message;

    super(safeMessage);
    this.name = 'TranslationProviderError';
    this.kind = options.kind;
    this.retryable = options.retryable ?? defaults.retryable;
    this.code = safeCode;
    this.safeCode = safeCode;
    this.safeMessage = safeMessage;
    this.cause = options.cause;
    Object.setPrototypeOf(this, new.target.prototype);
  }
}

export function isTranslationProviderError(error: unknown): error is TranslationProviderError {
  return error instanceof TranslationProviderError;
}

export function toTranslationProviderError(error: unknown): TranslationProviderError {
  if (isTranslationProviderError(error)) return error;

  const rawMessage = error instanceof Error ? error.message : '';
  const message = rawMessage.toLowerCase();
  let kind: TranslationProviderErrorKind = 'transient';

  if (
    message.includes('timed out') ||
    message.includes('timeout')
  ) {
    kind = 'timeout';
  } else if (
    message.includes('rate limit') ||
    message.includes('quota') ||
    message.includes('resource_exhausted') ||
    message.includes('too many requests') ||
    /\b429\b/.test(message)
  ) {
    kind = 'rate_limited';
  } else if (
    message.includes('empty') ||
    message.includes('malformed') ||
    message.includes('parse') ||
    message.includes('invalid response') ||
    message.includes('invalid json')
  ) {
    kind = 'malformed_response';
  } else if (
    message.includes('api key') ||
    message.includes('authentication failed') ||
    message.includes('unauthorized') ||
    message.includes('forbidden') ||
    message.includes('permission denied') ||
    /\b(?:401|403)\b/.test(message)
  ) {
    kind = 'permanent';
  } else if (
    message.includes('unavailable') ||
    message.includes('network') ||
    message.includes('econn') ||
    /\b(?:500|502|503|504)\b/.test(message)
  ) {
    kind = 'transient';
  }

  return new TranslationProviderError({ kind, cause: error });
}

export type ProviderResultKind = 'text' | 'structured' | 'bulk';
type ProviderResultRecord = Record<string, unknown>;

function malformedResponse(reason?: string): never {
  throw new TranslationProviderError({
    kind: 'malformed_response',
    message: reason
      ? `Translation provider returned an invalid ${reason} response.`
      : undefined,
  });
}

function asRecord(value: unknown): ProviderResultRecord {
  if (typeof value !== 'object' || value === null || Array.isArray(value)) {
    return malformedResponse();
  }
  return value as ProviderResultRecord;
}

function isNonNegativeInteger(value: unknown): value is number {
  return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;
}

function validateTokenAndModel(result: ProviderResultRecord, processingTime: boolean): void {
  const tokenFields = ['tokens_used', 'input_tokens', 'output_tokens'];
  for (const field of tokenFields) {
    if (!isNonNegativeInteger(result[field])) malformedResponse();
  }

  if (result.tokens_used !== (result.input_tokens as number) + (result.output_tokens as number)) {
    malformedResponse();
  }

  if (typeof result.model_used !== 'string' || result.model_used.trim() === '') {
    malformedResponse();
  }

  if (processingTime && !isNonNegativeInteger(result.processing_time_ms)) {
    malformedResponse();
  }
}

function validateTextResult(result: unknown): TranslationProviderTextResult {
  const record = asRecord(result);
  if (typeof record.translation !== 'string' || record.translation.trim() === '') {
    malformedResponse('text');
  }
  validateTokenAndModel(record, true);
  return record as unknown as TranslationProviderTextResult;
}

function validateStructuredResult(
  result: unknown,
  fields: StructuredTranslationFields | undefined
): TranslationProviderStructuredResult {
  const record = asRecord(result);
  validateTokenAndModel(record, true);

  const fieldNames: Array<keyof StructuredTranslationFields> = ['title', 'excerpt', 'content'];
  for (const field of fieldNames) {
    const inputPresent = fields ? fields[field] !== undefined : undefined;
    const outputKey = `translated_${field}`;
    const outputPresent = record[outputKey] !== undefined;

    if (
      inputPresent === true &&
      (!outputPresent || typeof record[outputKey] !== 'string' || (record[outputKey] as string).trim() === '')
    ) {
      malformedResponse('structured');
    }
    if (inputPresent === false && outputPresent) {
      malformedResponse('structured');
    }
    if (fields === undefined && outputPresent && typeof record[outputKey] !== 'string') {
      malformedResponse();
    }
  }

  return record as unknown as TranslationProviderStructuredResult;
}

function validateBulkResult(
  result: unknown,
  inputItems: readonly BulkStringItem[] | undefined
): TranslationProviderBulkResult {
  const record = asRecord(result);
  validateTokenAndModel(record, false);

  if (!Array.isArray(record.results)) malformedResponse();
  const results = record.results as unknown[];
  if (inputItems && results.length !== inputItems.length) malformedResponse();

  results.forEach((item, index) => {
    const itemRecord = asRecord(item);
    if (typeof itemRecord.id !== 'string') malformedResponse();
    if (inputItems && itemRecord.id !== inputItems[index].id) malformedResponse('bulk item order');
    if (typeof itemRecord.translation !== 'string') malformedResponse();
    if (typeof itemRecord.success !== 'boolean') malformedResponse();
    if (itemRecord.success && itemRecord.translation.trim() === '') malformedResponse();
    if (!itemRecord.success && itemRecord.translation !== '') malformedResponse();
    if (itemRecord.error !== undefined && typeof itemRecord.error !== 'string') malformedResponse();
  });

  return record as unknown as TranslationProviderBulkResult;
}

export interface ProviderValidationContext {
  kind: ProviderResultKind;
  fields?: StructuredTranslationFields;
  inputItems?: readonly BulkStringItem[];
}

export function validateProviderResult(
  kind: 'text',
  result: unknown
): TranslationProviderTextResult;
export function validateProviderResult(
  kind: 'structured',
  result: unknown,
  fields: StructuredTranslationFields
): TranslationProviderStructuredResult;
export function validateProviderResult(
  kind: 'bulk',
  result: unknown,
  inputItems: readonly BulkStringItem[]
): TranslationProviderBulkResult;
export function validateProviderResult(
  result: unknown,
  context: { kind: 'text' }
): TranslationProviderTextResult;
export function validateProviderResult(
  result: unknown,
  context: { kind: 'structured'; fields: StructuredTranslationFields }
): TranslationProviderStructuredResult;
export function validateProviderResult(
  result: unknown,
  context: { kind: 'bulk'; inputItems: readonly BulkStringItem[] }
): TranslationProviderBulkResult;
export function validateProviderResult(result: unknown, context: ProviderValidationContext):
  | TranslationProviderTextResult
  | TranslationProviderStructuredResult
  | TranslationProviderBulkResult;
export function validateProviderResult(
  first: ProviderResultKind | unknown,
  second: unknown,
  input?: StructuredTranslationFields | readonly BulkStringItem[]
): unknown {
  let kind: ProviderResultKind;
  let result: unknown;
  let validationInput = input;

  if (typeof first === 'string' && (first === 'text' || first === 'structured' || first === 'bulk')) {
    kind = first;
    result = second;
  } else {
    const context = second as ProviderValidationContext;
    if (!context || typeof context !== 'object' || !('kind' in context)) malformedResponse();
    kind = context.kind;
    result = first;
    validationInput = kind === 'structured' ? context.fields : context.inputItems;
  }

  switch (kind) {
    case 'text':
      return validateTextResult(result);
    case 'structured':
      return validateStructuredResult(result, validationInput as StructuredTranslationFields | undefined);
    case 'bulk':
      return validateBulkResult(result, validationInput as readonly BulkStringItem[] | undefined);
    default:
      return malformedResponse();
  }
}

export type TranslationExecutionMode = 'live' | 'mock';

export function getTranslationProvider(
  executionMode: TranslationExecutionMode,
  context: TranslationProviderContext = {}
): TranslationProvider {
  if (executionMode === 'live') return geminiTranslationProvider;
  if (executionMode !== 'mock') {
    throw new TranslationProviderError({
      kind: 'permanent',
      code: 'TRANSLATION_PROVIDER_MODE_INVALID',
      message: 'Translation provider mode is invalid.',
      retryable: false,
    });
  }
  if (process.env.MOCK_TRANSLATION_KILL_SWITCH === '1') {
    throw new TranslationProviderError({
      kind: 'permanent',
      code: 'MOCK_TRANSLATION_DISABLED',
      message: 'Mock translation is disabled.',
      retryable: false,
    });
  }
  return new MockTranslationProvider(context.store, context);
}
