/**
 * AI Patrol API client
 * Provides access to background AI monitoring findings and status
 */

import { apiFetchJSON } from '@/utils/apiClient';

export type FindingSeverity = 'info' | 'watch' | 'warning' | 'critical';
export type FindingCategory = 'performance' | 'capacity' | 'reliability' | 'backup' | 'security' | 'general';

export interface Finding {
    id: string;
    severity: FindingSeverity;
    category: FindingCategory;
    resource_id: string;
    resource_name: string;
    resource_type: string; // node, vm, container, docker_host, docker_container, storage, pbs, host_raid
    node?: string;
    title: string;
    description: string;
    recommendation?: string;
    evidence?: string;
    detected_at: string;
    last_seen_at: string;
    resolved_at?: string;
    auto_resolved: boolean;
    acknowledged_at?: string;
    snoozed_until?: string; // Finding hidden until this time
    alert_id?: string;
    // User feedback fields (LLM memory system)
    dismissed_reason?: 'not_an_issue' | 'expected_behavior' | 'will_fix_later';
    user_note?: string;
    times_raised: number;
    suppressed: boolean;
}

export interface FindingsSummary {
    critical: number;
    warning: number;
    watch: number;
    info: number;
}

export interface PatrolStatus {
    running: boolean;
    enabled: boolean;
    last_patrol_at?: string;
    last_deep_analysis_at?: string;
    next_patrol_at?: string;
    last_duration_ms: number;
    resources_checked: number;
    findings_count: number;
    error_count: number;
    healthy: boolean;
    interval_ms: number; // Patrol interval in milliseconds
    summary: FindingsSummary;
}

export interface PatrolRunRecord {
    id: string;
    started_at: string;
    completed_at: string;
    duration_ms: number;
    type: string; // Always 'patrol' now (kept for backwards compat with old records)
    resources_checked: number;
    // Breakdown by resource type
    nodes_checked: number;
    guests_checked: number;
    docker_checked: number;
    storage_checked: number;
    hosts_checked: number;
    pbs_checked: number;
    // Findings from this run
    new_findings: number;
    existing_findings: number;
    resolved_findings: number;
    findings_summary: string;
    finding_ids: string[];
    error_count: number;
    status: 'healthy' | 'issues_found' | 'critical' | 'error';
    // AI Analysis details
    ai_analysis?: string;    // The AI's raw response/analysis
    input_tokens?: number;   // Tokens sent to AI
    output_tokens?: number;  // Tokens received from AI
}

/**
 * Get the current AI patrol status
 */
export async function getPatrolStatus(): Promise<PatrolStatus> {
    const resp = await fetch('/api/ai/patrol/status', {
        credentials: 'include',
    });
    if (!resp.ok) {
        throw new Error(`Failed to get patrol status: ${resp.status}`);
    }
    return resp.json();
}

/**
 * Get all active findings from the patrol service
 * Optionally filter by resource ID
 */
export async function getFindings(resourceId?: string): Promise<Finding[]> {
    const url = resourceId
        ? `/api/ai/patrol/findings?resource_id=${encodeURIComponent(resourceId)}`
        : '/api/ai/patrol/findings';

    const resp = await fetch(url, {
        credentials: 'include',
    });
    if (!resp.ok) {
        throw new Error(`Failed to get findings: ${resp.status}`);
    }
    return resp.json();
}

/**
 * Trigger an immediate patrol run
 */
export async function forcePatrol(deep: boolean = false): Promise<{ success: boolean; message: string }> {
    const url = deep ? '/api/ai/patrol/run?deep=true' : '/api/ai/patrol/run';
    return apiFetchJSON(url, { method: 'POST' });
}

/**
 * Get AI findings history including resolved findings
 * @param startTime Optional ISO timestamp to filter findings from
 */
export async function getFindingsHistory(startTime?: string): Promise<Finding[]> {
    const url = startTime
        ? `/api/ai/patrol/history?start_time=${encodeURIComponent(startTime)}`
        : '/api/ai/patrol/history';
    const resp = await fetch(url, {
        method: 'GET',
        credentials: 'include',
    });
    if (!resp.ok) {
        throw new Error(`Failed to get findings history: ${resp.status}`);
    }
    return resp.json();
}

/**
 * Get the history of patrol check runs
 * @param limit Maximum number of records to return (default: 50, max: 100)
 */
export async function getPatrolRunHistory(limit?: number): Promise<PatrolRunRecord[]> {
    const url = limit
        ? `/api/ai/patrol/runs?limit=${limit}`
        : '/api/ai/patrol/runs';
    const resp = await fetch(url, {
        method: 'GET',
        credentials: 'include',
    });
    if (!resp.ok) {
        throw new Error(`Failed to get patrol run history: ${resp.status}`);
    }
    return resp.json();
}

/**
 * Acknowledge a finding (marks as seen but keeps visible, like alert acknowledgement)
 * Finding will auto-resolve when the underlying condition clears.
 */
export async function acknowledgeFinding(findingId: string): Promise<{ success: boolean; message: string }> {
    return apiFetchJSON('/api/ai/patrol/acknowledge', {
        method: 'POST',
        body: JSON.stringify({ finding_id: findingId }),
    });
}

/**
 * Snooze a finding for a specified duration
 * @param findingId The ID of the finding to snooze
 * @param durationHours Duration in hours (e.g., 1, 24, 168 for 7 days)
 */
export async function snoozeFinding(findingId: string, durationHours: number): Promise<{ success: boolean; message: string }> {
    return apiFetchJSON('/api/ai/patrol/snooze', {
        method: 'POST',
        body: JSON.stringify({ finding_id: findingId, duration_hours: durationHours }),
    });
}

/**
 * Manually resolve a finding (mark as fixed)
 * @param findingId The ID of the finding to resolve
 */
export async function resolveFinding(findingId: string): Promise<{ success: boolean; message: string }> {
    return apiFetchJSON('/api/ai/patrol/resolve', {
        method: 'POST',
        body: JSON.stringify({ finding_id: findingId }),
    });
}

/**
 * Dismiss a finding with a reason (LLM memory feature)
 * The LLM will be told not to re-raise this issue in future patrols.
 * @param findingId The ID of the finding to dismiss
 * @param reason One of: "not_an_issue", "expected_behavior", "will_fix_later"
 * @param note Optional freeform explanation
 */
export async function dismissFinding(
    findingId: string,
    reason: 'not_an_issue' | 'expected_behavior' | 'will_fix_later',
    note?: string
): Promise<{ success: boolean; message: string }> {
    return apiFetchJSON('/api/ai/patrol/dismiss', {
        method: 'POST',
        body: JSON.stringify({ finding_id: findingId, reason, note }),
    });
}

/**
 * Permanently suppress a finding type (LLM memory feature)
 * The LLM will never re-raise this type of finding for this resource.
 * @param findingId The ID of the finding to suppress
 */
export async function suppressFinding(findingId: string): Promise<{ success: boolean; message: string }> {
    return apiFetchJSON('/api/ai/patrol/suppress', {
        method: 'POST',
        body: JSON.stringify({ finding_id: findingId }),
    });
}

/**
 * Severity color mapping for UI
 */
export const severityColors: Record<FindingSeverity, { bg: string; text: string; border: string }> = {
    critical: { bg: 'rgba(220, 38, 38, 0.15)', text: '#ef4444', border: 'rgba(220, 38, 38, 0.3)' },
    warning: { bg: 'rgba(234, 179, 8, 0.15)', text: '#eab308', border: 'rgba(234, 179, 8, 0.3)' },
    watch: { bg: 'rgba(59, 130, 246, 0.15)', text: '#3b82f6', border: 'rgba(59, 130, 246, 0.3)' },
    info: { bg: 'rgba(107, 114, 128, 0.15)', text: '#9ca3af', border: 'rgba(107, 114, 128, 0.3)' },
};

/**
 * Category labels for UI
 */
export const categoryLabels: Record<FindingCategory, string> = {
    performance: 'Performance',
    capacity: 'Capacity',
    reliability: 'Reliability',
    backup: 'Backup',
    security: 'Security',
    general: 'General',
};

/**
 * Format a timestamp for display
 */
export function formatTimestamp(ts: string): string {
    const date = new Date(ts);
    const now = new Date();
    const diffMs = now.getTime() - date.getTime();
    const diffMins = Math.floor(diffMs / 60000);
    const diffHours = Math.floor(diffMs / 3600000);
    const diffDays = Math.floor(diffMs / 86400000);

    if (diffMins < 1) return 'just now';
    if (diffMins < 60) return `${diffMins}m ago`;
    if (diffHours < 24) return `${diffHours}h ago`;
    if (diffDays < 7) return `${diffDays}d ago`;
    return date.toLocaleDateString();
}

/**
 * Event types from patrol stream
 */
export interface PatrolStreamEvent {
    type: 'start' | 'content' | 'thinking' | 'phase' | 'complete' | 'error';
    content?: string;
    phase?: string;
    tokens?: number;
}

/**
 * Subscribe to live patrol stream via SSE
 * Returns an unsubscribe function
 */
export function subscribeToPatrolStream(
    onEvent: (event: PatrolStreamEvent) => void,
    onError?: (error: Error) => void
): () => void {
    const eventSource = new EventSource('/api/ai/patrol/stream', { withCredentials: true });

    eventSource.onmessage = (event) => {
        try {
            const data = JSON.parse(event.data) as PatrolStreamEvent;
            onEvent(data);
        } catch (e) {
            console.error('Failed to parse patrol stream event:', e);
        }
    };

    eventSource.onerror = () => {
        if (onError) {
            onError(new Error('Patrol stream connection error'));
        }
        eventSource.close();
    };

    // Return unsubscribe function
    return () => {
        eventSource.close();
    };
}

// === Suppression Rules ===

export interface SuppressionRule {
    id: string;
    resource_id?: string;   // Empty means "any resource"
    resource_name?: string; // Human-readable name
    category?: FindingCategory; // Empty means "any category"
    description: string;    // User's reason
    created_at: string;
    created_from: 'finding' | 'manual';
    finding_id?: string;    // Original finding ID if created from dismissal
}

/**
 * Get all suppression rules (both manual and from dismissed findings)
 */
export async function getSuppressionRules(): Promise<SuppressionRule[]> {
    const resp = await fetch('/api/ai/patrol/suppressions', {
        credentials: 'include',
    });
    if (!resp.ok) {
        throw new Error(`Failed to get suppression rules: ${resp.status}`);
    }
    return resp.json();
}

/**
 * Create a new manual suppression rule
 * @param resourceId Resource ID (empty for "any resource")
 * @param resourceName Human-readable name for display
 * @param category Category (empty for "any category")
 * @param description User's reason for the rule
 */
export async function addSuppressionRule(
    resourceId: string,
    resourceName: string,
    category: FindingCategory | '',
    description: string
): Promise<{ success: boolean; message: string; rule: SuppressionRule }> {
    return apiFetchJSON('/api/ai/patrol/suppressions', {
        method: 'POST',
        body: JSON.stringify({
            resource_id: resourceId,
            resource_name: resourceName,
            category: category,
            description: description,
        }),
    });
}

/**
 * Delete a suppression rule
 * @param ruleId The ID of the rule to delete
 */
export async function deleteSuppressionRule(ruleId: string): Promise<{ success: boolean; message: string }> {
    return apiFetchJSON(`/api/ai/patrol/suppressions/${encodeURIComponent(ruleId)}`, {
        method: 'DELETE',
    });
}

/**
 * Get all dismissed/suppressed findings
 */
export async function getDismissedFindings(): Promise<Finding[]> {
    const resp = await fetch('/api/ai/patrol/dismissed', {
        credentials: 'include',
    });
    if (!resp.ok) {
        throw new Error(`Failed to get dismissed findings: ${resp.status}`);
    }
    return resp.json();
}
