/**
 * PDF Queue storage utilities
 *
 * Manages a persistent list of detected PDFs with upload status tracking.
 * Uses chrome.storage.local for persistence across service worker restarts.
 */

import { DetectedPDF } from './pdf-detector';

const STORAGE_KEY = 'pdfQueue';
const MAX_ENTRIES = 50;
const SENT_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
const FAILED_MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours
const DISMISSED_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000; // 7 days

export type PDFStatus =
  | 'pending'       // Not yet uploaded
  | 'uploading'     // Upload in progress
  | 'failed'        // Upload failed
  | 'in_basket'     // Uploaded, SHIPPABLE, awaiting order
  | 'ordering'      // Order in progress
  | 'in_production' // PRODUCTION_QUEUE or PRINTING
  | 'sent'          // SENT - terminal
  | 'canceled'      // CANCELED - terminal
  | 'dismissed';    // User dismissed, don't show again

export interface PDFQueueEntry extends DetectedPDF {
  status: PDFStatus;
  binectDocumentId?: number;
  binectStatus?: number;        // DocumentStatus code from Binect (1-7)
  binectStatusText?: string;    // Human-readable status from Binect
  price?: number;               // Price in euro cents
  recipientAddress?: string;    // Extracted recipient address
  errorMessage?: string;
  uploadedAt?: number;
  orderedAt?: number;
}

interface PDFQueueState {
  entries: PDFQueueEntry[];
  lastUpdated: number;
}

/**
 * Load queue from storage
 */
export async function loadQueue(): Promise<PDFQueueState> {
  const result = await chrome.storage.local.get(STORAGE_KEY);
  if (result[STORAGE_KEY]) {
    return result[STORAGE_KEY] as PDFQueueState;
  }
  return { entries: [], lastUpdated: Date.now() };
}

/**
 * Save queue to storage
 */
export async function saveQueue(state: PDFQueueState): Promise<void> {
  state.lastUpdated = Date.now();
  await chrome.storage.local.set({ [STORAGE_KEY]: state });
}

/**
 * Add a PDF to the queue
 *
 * Returns the created entry, or null if the PDF was already uploaded.
 * If the PDF already exists as pending/failed, returns the existing entry.
 */
export async function addPDF(pdf: DetectedPDF): Promise<PDFQueueEntry | null> {
  const state = await loadQueue();

  // Check for duplicate by URL
  const existing = state.entries.find(e => e.url === pdf.url);
  if (existing) {
    // Skip if already processed (uploaded, dismissed, etc.)
    const processedStatuses: PDFStatus[] = ['in_basket', 'ordering', 'in_production', 'sent', 'canceled', 'dismissed'];
    if (processedStatuses.includes(existing.status)) {
      console.log('[PDF Queue] PDF already processed, skipping:', pdf.filename, existing.status);
      return null;
    }
    console.log('[PDF Queue] PDF already in queue:', pdf.filename);
    return existing;
  }

  // Create new entry
  const entry: PDFQueueEntry = {
    ...pdf,
    status: 'pending'
  };

  // Add to beginning (most recent first)
  state.entries.unshift(entry);

  // Enforce max entries
  await enforceMaxEntries(state);

  await saveQueue(state);
  console.log('[PDF Queue] Added PDF:', pdf.filename);
  return entry;
}

/**
 * Metadata for status updates
 */
export interface PDFStatusMeta {
  binectDocumentId?: number;
  binectStatus?: number;
  binectStatusText?: string;
  price?: number;
  recipientAddress?: string;
  errorMessage?: string;
}

/**
 * Update the status of a PDF in the queue
 */
export async function updatePDFStatus(
  id: string,
  status: PDFStatus,
  meta?: PDFStatusMeta
): Promise<void> {
  const state = await loadQueue();
  const entry = state.entries.find(e => e.id === id);

  if (!entry) {
    console.warn('[PDF Queue] PDF not found for status update:', id);
    return;
  }

  entry.status = status;

  // Update Binect-specific fields
  if (meta?.binectDocumentId !== undefined) {
    entry.binectDocumentId = meta.binectDocumentId;
  }
  if (meta?.binectStatus !== undefined) {
    entry.binectStatus = meta.binectStatus;
  }
  if (meta?.binectStatusText !== undefined) {
    entry.binectStatusText = meta.binectStatusText;
  }
  if (meta?.price !== undefined) {
    entry.price = meta.price;
  }
  if (meta?.recipientAddress !== undefined) {
    entry.recipientAddress = meta.recipientAddress;
  }
  if (meta?.errorMessage !== undefined) {
    entry.errorMessage = meta.errorMessage;
  }

  // Set timestamps based on status
  if (status === 'in_basket' && !entry.uploadedAt) {
    entry.uploadedAt = Date.now();
  }
  if (status === 'in_production' && !entry.orderedAt) {
    entry.orderedAt = Date.now();
  }

  await saveQueue(state);
  console.log('[PDF Queue] Updated status:', id, status);
}

/**
 * Dismiss a PDF (mark as dismissed so it won't reappear)
 */
export async function dismissPDF(id: string): Promise<void> {
  const state = await loadQueue();
  const entry = state.entries.find(e => e.id === id);

  if (!entry) {
    console.warn('[PDF Queue] PDF not found for dismissal:', id);
    return;
  }

  entry.status = 'dismissed';
  await saveQueue(state);
  console.log('[PDF Queue] Dismissed PDF:', id);
}

/**
 * Remove a PDF from the queue (complete removal, used for cleanup)
 */
export async function removePDF(id: string): Promise<void> {
  const state = await loadQueue();
  const index = state.entries.findIndex(e => e.id === id);

  if (index === -1) {
    console.warn('[PDF Queue] PDF not found for removal:', id);
    return;
  }

  state.entries.splice(index, 1);
  await saveQueue(state);
  console.log('[PDF Queue] Removed PDF:', id);
}

/**
 * Get all PDFs for display in popup (excludes dismissed)
 */
export async function getAllPDFs(): Promise<PDFQueueEntry[]> {
  const state = await loadQueue();
  // Filter out dismissed entries - they're kept for duplicate detection but not displayed
  return state.entries.filter(e => e.status !== 'dismissed');
}

/**
 * Get PDFs that need user action (pending, failed, in_basket)
 */
export async function getActionablePDFs(): Promise<PDFQueueEntry[]> {
  const state = await loadQueue();
  return state.entries.filter(e =>
    e.status === 'pending' ||
    e.status === 'failed' ||
    e.status === 'in_basket'
  );
}

/**
 * Get count of PDFs needing action (for badge)
 */
export async function getActionableCount(): Promise<number> {
  const actionable = await getActionablePDFs();
  return actionable.length;
}

/**
 * Legacy: Get pending PDFs (for backward compatibility)
 */
export async function getPendingPDFs(): Promise<PDFQueueEntry[]> {
  return getActionablePDFs();
}

/**
 * Legacy: Get pending count (for backward compatibility)
 */
export async function getPendingCount(): Promise<number> {
  return getActionableCount();
}

/**
 * Clean up old entries to prevent unbounded growth
 */
export async function cleanupOldEntries(): Promise<void> {
  const state = await loadQueue();
  const now = Date.now();
  const initialCount = state.entries.length;

  state.entries = state.entries.filter(entry => {
    // Always keep active entries
    if (
      entry.status === 'pending' ||
      entry.status === 'uploading' ||
      entry.status === 'in_basket' ||
      entry.status === 'ordering' ||
      entry.status === 'in_production'
    ) {
      return true;
    }

    // Remove sent/canceled entries older than 7 days
    if (entry.status === 'sent' || entry.status === 'canceled') {
      const age = now - (entry.orderedAt || entry.uploadedAt || entry.timestamp);
      if (age > SENT_MAX_AGE_MS) {
        return false;
      }
    }

    // Remove failed entries older than 24 hours
    if (entry.status === 'failed') {
      const age = now - entry.timestamp;
      if (age > FAILED_MAX_AGE_MS) {
        return false;
      }
    }

    // Remove dismissed entries older than 7 days
    if (entry.status === 'dismissed') {
      const age = now - entry.timestamp;
      if (age > DISMISSED_MAX_AGE_MS) {
        return false;
      }
    }

    return true;
  });

  if (state.entries.length < initialCount) {
    await saveQueue(state);
    console.log('[PDF Queue] Cleaned up', initialCount - state.entries.length, 'old entries');
  }
}

/**
 * Enforce maximum entries by removing oldest terminal entries
 */
async function enforceMaxEntries(state: PDFQueueState): Promise<void> {
  const terminalStatuses: PDFStatus[] = ['sent', 'canceled', 'failed'];

  while (state.entries.length > MAX_ENTRIES) {
    let removeIndex = -1;
    let oldestTime = Infinity;

    // Find oldest terminal entry (sent, canceled, failed)
    for (let i = state.entries.length - 1; i >= 0; i--) {
      const entry = state.entries[i];
      if (terminalStatuses.includes(entry.status)) {
        const entryTime = entry.orderedAt || entry.uploadedAt || entry.timestamp;
        if (entryTime < oldestTime) {
          oldestTime = entryTime;
          removeIndex = i;
        }
      }
    }

    // If no terminal entries, we can't remove any more
    if (removeIndex === -1) {
      console.warn('[PDF Queue] Max entries reached, but all are active');
      break;
    }

    state.entries.splice(removeIndex, 1);
  }
}
