import * as vscode from 'vscode';

interface AbysiusConfig {
    apiKey: string;
    chatEndpoint: string;
    inlineEndpoint: string;
    model: string;
}

export interface InlineRequest {
    prompt: string;
    suffix: string;
    language: string;
    filename: string;
    max_tokens?: number;
}

export interface InlineResponse {
    completion: string;
    finish_reason: string;
    suggestionId?: string;
    similarityToTraining?: number;
    nearestNeighborRepo?: string | null;
    confidence?: 'original' | 'derivative' | 'verbatim_fragment';
}

export interface ChatMessage {
    role: 'user' | 'assistant' | 'system';
    content: string;
}

export interface ChatRequest {
    messages: ChatMessage[];
    model?: string;
    stream?: boolean;
    temperature?: number;
    max_tokens?: number;
}

export interface ChatResponse {
    message: ChatMessage;
    finish_reason: string;
    usage?: {
        prompt_tokens: number;
        completion_tokens: number;
        total_tokens: number;
    };
}

export interface AuditEntry {
    timestamp: number;
    type: 'inline' | 'chat';
    suggestionId?: string;
    similarityToTraining?: number;
    nearestNeighborRepo?: string | null;
    confidence?: 'original' | 'derivative' | 'verbatim_fragment';
    accepted?: boolean;
    blockedGeo?: boolean;
    geoCountry?: string;
    error?: string;
}

export interface AbysiusConfig {
    apiKey: string;
    chatEndpoint: string;
    inlineEndpoint: string;
    model: string;
    dataResidency?: 'auto' | 'us' | 'eu' | 'asia';
    enableAuditLog?: boolean;
    geoBlockRestrictedRegions?: boolean;
    anonymizeFilePaths?: boolean;
}

export class AbysiusApi {
    private config: AbysiusConfig;
    private auditLog: AuditEntry[] = [];
    private readonly RESTRICTED_COUNTRIES = ['CU', 'IR', 'KP', 'RU', 'SY'];

    constructor(config: AbysiusConfig) {
        this.config = config;
    }

    updateConfig(config: Partial<AbysiusConfig>) {
        this.config = { ...this.config, ...config };
    }

    private getHeaders(): Record<string, string> {
        const headers: Record<string, string> = {
            'Content-Type': 'application/json',
            'User-Agent': 'AbysiusCodium/0.1.0'
        };
        if (this.config.apiKey) {
            headers['Authorization'] = `Bearer ${this.config.apiKey}`;
        }
        if (this.config.dataResidency && this.config.dataResidency !== 'auto') {
            headers['X-Data-Residency'] = this.config.dataResidency;
        }
        return headers;
    }

    private async geoCheck(): Promise<{ blocked: boolean; country?: string }> {
        if (this.config.geoBlockRestrictedRegions === false) {
            return { blocked: false };
        }
        // In production, derive from API response or client IP context.
        // Returning blocked=false here; server implements actual geo-blocking.
        return { blocked: false };
    }

    private writeAudit(entry: AuditEntry): void {
        if (!this.config.enableAuditLog) return;
        this.auditLog.push(entry);
        // Trim audit log to last 10,000 entries to prevent unbounded growth
        if (this.auditLog.length > 10000) {
            this.auditLog = this.auditLog.slice(-10000);
        }
        // In production, optionally persist to file or stream to a compliance endpoint
    }

    getAuditLog(): ReadonlyArray<AuditEntry> {
        return this.auditLog;
    }

    clearAuditLog(): void {
        this.auditLog = [];
    }

    private anonymizeFilename(filename: string): string {
        if (this.config.anonymizeFilePaths !== false) {
            return filename.split(/[\\/]/).pop() || filename;
        }
        return filename;
    }

    async getInlineCompletion(request: InlineRequest, signal?: AbortSignal): Promise<InlineResponse | null> {
        try {
            const config = vscode.workspace.getConfiguration('abysius');
            const maxLength = config.get<number>('inlineCompletionMaxLength', 200);

            const response = await fetch(this.config.inlineEndpoint, {
                method: 'POST',
                headers: this.getHeaders(),
                body: JSON.stringify({
                    ...request,
                    model: this.config.model,
                    max_tokens: Math.min(request.max_tokens || maxLength, maxLength)
                }),
                signal
            });

            if (!response.ok) {
                const error = await response.text();
                console.error('[Abysius API] Inline completion error:', error);
                return null;
            }

            return await response.json() as InlineResponse;
        } catch (err) {
            if (err instanceof Error && err.name === 'AbortError') {
                return null;
            }
            console.error('[Abysius API] Inline completion failed:', err);
            return null;
        }
    }

    async *streamChat(request: ChatRequest, signal?: AbortSignal): AsyncGenerator<string, ChatResponse | null, unknown> {
        try {
            const response = await fetch(this.config.chatEndpoint, {
                method: 'POST',
                headers: this.getHeaders(),
                body: JSON.stringify({
                    ...request,
                    model: this.config.model,
                    stream: true
                }),
                signal
            });

            if (!response.ok) {
                const error = await response.text();
                console.error('[Abysius API] Chat error:', error);
                return null;
            }

            const reader = response.body?.getReader();
            if (!reader) return null;

            const decoder = new TextDecoder();
            let buffer = '';

            try {
                while (true) {
                    const { done, value } = await reader.read();
                    if (done) break;

                    buffer += decoder.decode(value, { stream: true });
                    const lines = buffer.split('\n');
                    buffer = lines.pop() || '';

                    for (const line of lines) {
                        const trimmed = line.trim();
                        if (!trimmed || !trimmed.startsWith('data: ')) continue;
                        
                        const data = trimmed.slice(6);
                        if (data === '[DONE]') continue;

                        try {
                            const chunk = JSON.parse(data);
                            const delta = chunk.choices?.[0]?.delta?.content;
                            if (delta) {
                                yield delta;
                            }
                        } catch {
                            // ignore malformed JSON
                        }
                    }
                }
            } finally {
                reader.releaseLock();
            }

            return null;
        } catch (err) {
            if (err instanceof Error && err.name === 'AbortError') {
                return null;
            }
            console.error('[Abysius API] Chat stream failed:', err);
            return null;
        }
    }

    async sendChat(request: ChatRequest, signal?: AbortSignal): Promise<ChatResponse | null> {
        try {
            const response = await fetch(this.config.chatEndpoint, {
                method: 'POST',
                headers: this.getHeaders(),
                body: JSON.stringify({
                    ...request,
                    model: this.config.model,
                    stream: false
                }),
                signal
            });

            if (!response.ok) {
                const error = await response.text();
                console.error('[Abysius API] Chat error:', error);
                return null;
            }

            return await response.json() as ChatResponse;
        } catch (err) {
            if (err instanceof Error && err.name === 'AbortError') {
                return null;
            }
            console.error('[Abysius API] Chat failed:', err);
            return null;
        }
    }
}
