/**
 * Builder error sanitization utilities.
 * Maps raw error strings from the Go builder to user-friendly, translatable messages.
 *
 * Error patterns are based on the builder's error classification system
 * (webby-builder/internal/models/errors.go). Errors arrive as plain text
 * in the format "anthropic error: ..." or "openai error: ..." wrapping
 * the upstream API error.
 *
 * IMPORTANT: Each `message` string below doubles as the i18n translation key
 * in lang/{locale}/chat.json. If you change a message here, you MUST update
 * the corresponding key in ALL locale chat.json files, or translations will
 * silently fall back to the English key.
 */

interface ErrorPattern {
    /** Regex to test against the raw error string */
    pattern: RegExp;
    /** User-friendly message (also serves as the i18n translation key) */
    message: string;
}

// Order matters: more specific patterns first to avoid false matches
const ERROR_PATTERNS: ErrorPattern[] = [
    // Rate limit (429)
    {
        pattern: /\b429\b|rate limit|too many requests|limit exhausted/i,
        message: 'The AI service is currently rate limited. Please wait a moment and try again.',
    },
    // Model missing or not granted to the account.
    //
    // MUST stay ahead of the auth pattern below. Providers refuse a model the
    // account isn't entitled to with a 403 (e.g. OpenAI's "Project ... does not
    // have access to model ..." / "must be verified to use the model ..."), so
    // matching auth first reported a working API key as an authentication
    // failure and sent operators chasing their credentials instead of the model
    // configured on the provider.
    {
        pattern: /model[_ ]?not[_ ]?found|does not exist|access to (?:the )?model|(?:unsupported|unknown|invalid) model|verified to use the model/i,
        message: 'The AI model is not available. Please contact support.',
    },
    // Authentication / API key errors (401, 403)
    {
        pattern: /\b401\b|unauthorized|\b403\b|forbidden|invalid.?api.?key|invalid_api_key/i,
        message: 'There was an authentication issue with the AI service. Please contact support if this persists.',
    },
    // Context / token limit
    {
        pattern: /context length|maximum context|token limit|input too long|prompt too long|max tokens/i,
        message: 'The conversation has become too long for the AI to process. Try starting a new conversation.',
    },
    // Content filter / safety
    {
        pattern: /content.?filter|safety|moderation|blocked|flagged/i,
        message: 'Your request was flagged by the content filter. Please rephrase your message and try again.',
    },
    // Connection errors
    {
        pattern: /connection reset|connection refused|\beof\b|broken pipe/i,
        message: 'Lost connection to the AI service. Please try again.',
    },
    // Timeout
    {
        pattern: /timeout|timed out/i,
        message: 'The AI service took too long to respond. Please try again.',
    },
    // Server errors (500-504)
    {
        pattern: /\b50[0-4]\b|internal server error|bad gateway|service unavailable|gateway timeout/i,
        message: 'The AI service is temporarily unavailable. Please try again in a few moments.',
    },
    // Overloaded / capacity
    {
        pattern: /overloaded|capacity/i,
        message: 'The AI service is experiencing high demand. Please try again shortly.',
    },
    // Bad request (400)
    {
        pattern: /\b400\b|bad request/i,
        message: 'The request could not be processed. Please try again.',
    },
    // Build credits exhausted
    {
        pattern: /\bcredits?\b.*(?:exhausted|depleted|exceeded|insufficient|run\s*out|remaining|reset)|no\s+credits?\b|\bcredit\s+balance\b/i,
        message: "You've run out of build credits. Please upgrade your plan or wait for your credits to reset.",
    },
    // No builders available
    {
        pattern: /no.*builders?.*available/i,
        message: 'No build servers are currently available. Please try again later.',
    },
];

const FALLBACK_MESSAGE = 'Something went wrong. Please try again.';

/** Broadcast-auth rejections: an expired session, a rotated CSRF token, lost access. */
export const SUBSCRIPTION_AUTH_ERROR_MESSAGE =
    'Could not connect to live build updates. You may need to sign in again.';

/** Any other reason the private subscription never joined. */
export const SUBSCRIPTION_ERROR_MESSAGE =
    'Could not connect to live build updates. The build is still running and will finish in the background.';

/**
 * Maps a `pusher:subscription_error` payload to user-facing copy.
 *
 * The build stream is a private channel, so a subscription can now fail where
 * it previously could not: /broadcasting/auth rejects an idle tab past
 * SESSION_LIFETIME, a logout in another tab, a rotated CSRF token, or lost
 * project access. The chat still shows a "running" build (that state comes from
 * progress.status, not the socket), so without this the user watches an
 * apparently dead UI with no explanation.
 *
 * The returned string doubles as the i18n translation key — the caller runs it
 * through t(). Both keys live in lang/{locale}/chat.json.
 */
export function subscriptionErrorMessage(status?: unknown): string {
    return status === 401 || status === 403 || status === 419
        ? SUBSCRIPTION_AUTH_ERROR_MESSAGE
        : SUBSCRIPTION_ERROR_MESSAGE;
}

/**
 * Every user-facing string this module can emit. Each one doubles as its i18n
 * key in lang/{locale}/chat.json.
 *
 * These are handed to `t()` as a *variable*, so a static scan of literal `t(...)`
 * arguments cannot see them — TranslationKeyCoverageTest cannot cover this file.
 * The list exists so a test can, and so a message added below cannot ship
 * without a translation.
 */
export const BUILDER_ERROR_MESSAGES: readonly string[] = [
    ...ERROR_PATTERNS.map(({ message }) => message),
    FALLBACK_MESSAGE,
    SUBSCRIPTION_AUTH_ERROR_MESSAGE,
    SUBSCRIPTION_ERROR_MESSAGE,
];

/**
 * Sanitizes a raw builder error string into a user-friendly, translatable message.
 * Logs the original raw error to console for debugging.
 *
 * @param rawError - The raw error string from the builder
 * @param t - Translation function from LanguageContext
 * @returns A user-friendly error message (translated if available)
 */
export function sanitizeBuilderError(
    rawError: string,
    t: (key: string) => string
): string {
    // Defensive guard: ensure rawError is a string (WebSocket payloads are untyped at runtime)
    const errorString = typeof rawError === 'string' ? rawError : String(rawError ?? '');

    // Log raw error for debugging
    console.error('[Builder Error]', errorString);

    for (const { pattern, message } of ERROR_PATTERNS) {
        if (pattern.test(errorString)) {
            return t(message);
        }
    }

    return t(FALLBACK_MESSAGE);
}
