import type { AgentReview, SubReview, AgentName, PDFPage } from '../types';
import { IPublicClientApplication } from '@azure/msal-browser';
import { getAccessToken } from './authService';

const HTTP_URL = import.meta.env.VITE_BACKEND_URL || 'http://localhost:8000';

const POLL_INTERVAL_MS = 2000;

/**
 * Options for proof analysis with optional database persistence.
 */
export interface AnalyzeProofOptions {
    campaignId?: string;
    proofName?: string;
    channel?: string;
    subChannel?: string;
    proofType?: string;
    /** Brand to use for brand guidelines analysis: 'Barclays' or 'Barclaycard' */
    brand?: string;
}

/**
 * Result of proof analysis, including optional database IDs if persisted.
 */
export interface AnalyzeProofResult {
    review: AgentReview;
    proofId?: string;
    versionId?: string;
    pdfPages?: PDFPage[];
    isIdenticalFile?: boolean;
}

/** Read a File as base64 string (without the data-url prefix). */
async function fileToBase64(file: File): Promise<string> {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onloadend = () => resolve((reader.result as string).split(',')[1]);
        reader.onerror = () => reject(new Error('Failed to read file'));
        reader.readAsDataURL(file);
    });
}

/** Sleep for `ms` milliseconds. */
const sleep = (ms: number) => new Promise<void>(resolve => setTimeout(resolve, ms));

/** Authenticated fetch helper — adds Bearer token. */
async function authFetch(
    url: string,
    init: RequestInit,
    accessToken: string,
): Promise<Response> {
    return fetch(url, {
        ...init,
        headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${accessToken}`,
            ...(init.headers ?? {}),
        },
    });
}

/**
 * Analyze a proof using the backend REST API.
 * Provides real-time updates as each agent completes via polling.
 */
export const analyzeProof = async (
    file: File,
    onAgentUpdate: (name: AgentName | 'Summary', review?: SubReview) => void,
    msalInstance: IPublicClientApplication,
    options?: AnalyzeProofOptions,
    onNotification?: (message: string) => void,
): Promise<AnalyzeProofResult> => {
    const accessToken = await getAccessToken(msalInstance);
    if (!accessToken) {
        throw new Error('Failed to acquire access token. Please sign in again.');
    }

    const fileData = await fileToBase64(file);

    // Submit the analysis job
    const submitRes = await authFetch(
        `${HTTP_URL}/api/analyze`,
        {
            method: 'POST',
            body: JSON.stringify({
                file_data: fileData,
                file_type: file.type,
                is_wip: false,
                campaign_id: options?.campaignId,
                proof_name: options?.proofName,
                channel: options?.channel,
                sub_channel: options?.subChannel,
                proof_type: options?.proofType,
                brand: options?.brand ?? 'Barclaycard',
            }),
        },
        accessToken,
    );

    if (!submitRes.ok) {
        const err = await submitRes.text();
        throw new Error(`Failed to submit analysis: ${submitRes.status} ${err}`);
    }

    const { job_id } = await submitRes.json();

    // Poll until complete
    const seenAgents = new Set<string>();
    let notifiedFallback = false;

    while (true) {
        await sleep(POLL_INTERVAL_MS);

        const pollRes = await authFetch(
            `${HTTP_URL}/api/analyze/${job_id}`,
            { method: 'GET' },
            accessToken,
        );

        if (!pollRes.ok) {
            throw new Error(`Polling failed: ${pollRes.status}`);
        }

        const job = await pollRes.json();

        // Notify about model fallback once
        if (job.model_fallback && !notifiedFallback) {
            notifiedFallback = true;
            onNotification?.('The primary AI model is currently unavailable. Analysis is continuing with the backup model and may take longer than usual.');
        }

        // Fire callbacks for newly completed agents
        for (const agentName of Object.keys(job.agents_completed ?? {})) {
            if (!seenAgents.has(agentName)) {
                seenAgents.add(agentName);
                onAgentUpdate(agentName as AgentName, job.agents_completed[agentName] as SubReview);
            }
        }

        if (job.status === 'complete') {
            return {
                review: job.result as AgentReview,
                proofId: job.proof_id ?? undefined,
                versionId: job.version_id ?? undefined,
                pdfPages: job.pdf_pages as PDFPage[] | undefined,
                isIdenticalFile: job.is_identical_file ?? undefined,
            };
        }

        if (job.status === 'error') {
            throw new Error(job.error_message || 'Analysis failed');
        }
    }
};

/**
 * Analyze a work-in-progress proof.
 * Returns a conversational summary for the creative team.
 */
export const analyzeWIPProof = async (
    file: File,
    onAgentUpdate: (name: AgentName | 'Summary', review?: SubReview) => void,
    msalInstance: IPublicClientApplication
): Promise<string> => {
    const accessToken = await getAccessToken(msalInstance);
    if (!accessToken) {
        throw new Error('Failed to acquire access token. Please sign in again.');
    }

    const fileData = await fileToBase64(file);

    // Submit the analysis job
    const submitRes = await authFetch(
        `${HTTP_URL}/api/analyze`,
        {
            method: 'POST',
            body: JSON.stringify({
                file_data: fileData,
                file_type: file.type,
                is_wip: true,
            }),
        },
        accessToken,
    );

    if (!submitRes.ok) {
        const err = await submitRes.text();
        throw new Error(`Failed to submit WIP analysis: ${submitRes.status} ${err}`);
    }

    const { job_id } = await submitRes.json();

    // Poll until complete
    const seenAgents = new Set<string>();

    while (true) {
        await sleep(POLL_INTERVAL_MS);

        const pollRes = await authFetch(
            `${HTTP_URL}/api/analyze/${job_id}`,
            { method: 'GET' },
            accessToken,
        );

        if (!pollRes.ok) {
            throw new Error(`Polling failed: ${pollRes.status}`);
        }

        const job = await pollRes.json();

        // Fire callbacks for newly completed agents
        for (const agentName of Object.keys(job.agents_completed ?? {})) {
            if (!seenAgents.has(agentName)) {
                seenAgents.add(agentName);
                onAgentUpdate(agentName as AgentName, job.agents_completed[agentName] as SubReview);
            }
        }

        if (job.status === 'complete') {
            return job.result?.leadAgentSummary || 'Analysis complete.';
        }

        if (job.status === 'error') {
            throw new Error(job.error_message || 'Analysis failed');
        }
    }
};

/**
 * Get a chat response from the WIP Lead Agent.
 * Uses HTTP REST endpoint.
 */
export const getWIPChatResponse = async (prompt: string): Promise<string> => {
    try {
        const response = await fetch(`${HTTP_URL}/api/chat`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ prompt })
        });

        if (!response.ok) {
            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
        }

        const data = await response.json();
        return data.response || data.message || 'No response from Lead Agent.';
    } catch (error) {
        console.error('Error getting WIP chat response:', error);
        return "I'm sorry, the chat feature requires the backend chat endpoint to be implemented. For now, please use the proof analysis feature.";
    }
};
