import { HoSo, MauHoSo, NguoiDung, TrangThaiHoSo, VaiTro, QuyenHan, NhatKy } from './types';
import { GoogleGenAI, Type } from "@google/genai";
import * as pdfjsLib from 'pdfjs-dist';

// Khởi tạo worker URL một lần
let workerInitialized = false;

/**
 * PDF Processor: Chuyên xử lý các thao tác liên quan đến file PDF
 */
class PdfProcessor {
  static initialize(version: string = '4.0.379'): void {
    if (typeof window === 'undefined' || workerInitialized) return;

    let basePath = '/';
    if (typeof window !== 'undefined' && window.location.pathname) {
      if (window.location.pathname.startsWith('/dev/')) {
        basePath = '/dev/';
      } else {
        basePath = (import.meta as any).env.BASE_URL || '/';
      }
    }

    const cleanBasePath = basePath === '/' ? '' : (basePath.endsWith('/') ? basePath.slice(0, -1) : basePath);
    const localWorkerPath = `${cleanBasePath}/pdf.worker.min.js`;

    (pdfjsLib as any).GlobalWorkerOptions.workerSrc = localWorkerPath;
    workerInitialized = true;
  }

  static async toImages(file: File, scale: number = 3.0): Promise<File[]> {
    try {
      this.initialize();

      const arrayBuffer = await file.arrayBuffer();
      let loadingTask = (pdfjsLib as any).getDocument({ data: arrayBuffer });
      let pdf;

      try {
        pdf = await loadingTask.promise;
      } catch (workerError: any) {
        console.warn('Local PDF worker failed, switching to CDN...');
        const version = (pdfjsLib as any).version || '4.0.379';
        (pdfjsLib as any).GlobalWorkerOptions.workerSrc = `https://cdn.jsdelivr.net/npm/pdfjs-dist@${version}/build/pdf.worker.min.mjs`;
        pdf = await (pdfjsLib as any).getDocument({ data: arrayBuffer }).promise;
      }

      const imageFiles: File[] = [];

      for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
        const page = await pdf.getPage(pageNum);
        const viewport = page.getViewport({ scale });

        const canvas = document.createElement('canvas');
        const context = canvas.getContext('2d');
        if (!context) throw new Error('Canvas context initialization failed');

        canvas.height = viewport.height;
        canvas.width = viewport.width;

        // QUAN TRỌNG: Vẽ nền trắng (mặc định canvas là trong suốt, có thể gây lỗi khi AI đọc)
        context.fillStyle = '#FFFFFF';
        context.fillRect(0, 0, canvas.width, canvas.height);

        context.imageSmoothingEnabled = true;
        context.imageSmoothingQuality = 'high';

        await page.render({ canvasContext: context, viewport }).promise;

        const blob = await new Promise<Blob>((resolve) => {
          canvas.toBlob((b) => resolve(b!), 'image/png', 1.0);
        });

        imageFiles.push(new File(
          [blob],
          `${file.name.replace('.pdf', '')}_p${pageNum}.png`,
          { type: 'image/png' }
        ));
      }

      return imageFiles;
    } catch (error: any) {
      throw new Error(`PDF Conversion Error: ${error.message}`);
    }
  }
}

/**
 * OCR Prompt Engine: Xây dựng các prompt chuyên nghiệp cho AI
 */
class OcrPromptEngine {
  static build(template: MauHoSo, numPages: number): string {
    const fieldGuidance = template.fields.map(f => {
      const key = f.key;
      const label = f.label;
      const lowerLabel = label.toLowerCase();

      let hints = [label];
      if (lowerLabel.includes('tên') || lowerLabel.includes('đơn vị')) hints.push('Tên người mua', 'Đơn vị mua hàng');
      if (lowerLabel.includes('thuế') || lowerLabel.includes('mst')) hints.push('Mã số thuế', 'MST');
      if (lowerLabel.includes('địa chỉ')) hints.push('Địa chỉ', 'Trụ sở');

      return `<field key="${key}" label="${label}">
  <hints>${hints.join(', ')}</hints>
  <scope>EXTRACT FROM BUYER SECTION ONLY</scope>
</field>`;
    }).join('\n');

    return `### ROLE: EXPERT VIETNAMESE DOCUMENT OCR & DATA EXTRACTOR
You are a specialist in digitizing Vietnamese administrative documents and e-invoices.

### CRITICAL RULES:
1. **IDENTIFY ENTITIES FIRST**:
   - SELLER (Bên bán): Usually at the VERY TOP. Often has a Logo.
   - BUYER (Bên mua): Usually in the MIDDLE. Look for labels like "Tên người mua", "Đơn vị mua hàng", "Mã số thuế người mua".

2. **STRICT ISOLATION**:
   - You MUST IGNORE the SELLER section entirely.
   - If you see "Mã số thuế: 0309320612" or "61 Cao Đức Lân" -> This is the SELLER. IGNORE IT.
   - You MUST extract the BUYER info. Look for another "Mã số thuế" and "Tên công ty" in the Buyer section below.

### EXTRACTION STEPS (CHAIN OF THOUGHT):
Step 1: Locate the "Bên Bán" (Seller) and note its info (to avoid it).
Step 2: Locate the "Bên Mua" (Buyer) section.
Step 3: Extract fields ONLY from the Buyer section identified in Step 2.
Step 4: Verify the "Mã số thuế" extracted matches the one in Buyer section, not Seller section.

### FIELDS TO EXTRACT:
${fieldGuidance}

### OUTPUT FORMAT:
Return a valid JSON object only.
{
  "result": {
    "KEY_NAME": "EXTRACTED_VALUE",
    ...
  },
  "confidence": {
    "KEY_NAME": 0.00 to 1.00,
    ...
  }
}

### FINAL VERIFICATION:
- Is the extracted Tax Code from the "Bên Mua" section?
- Does it have 10 or 13 digits?
- Is the Buyer Name exactly as written in the middle part of the invoice?`;
  }
}

// Hàm để khởi tạo worker với nhiều phương án fallback
function initializePdfWorker(): void {
  PdfProcessor.initialize();
}

const API_BASE_URL = (import.meta as any).env.VITE_API_URL || '/api';
const STORAGE_KEYS = {
  USER_SESSION: 'user',
};

const DEFAULT_PERMISSIONS: QuyenHan[] = [
  { id: 'VIEW_DASHBOARD', nhom: 'Hệ thống', ten: 'Xem Tổng quan', moTa: 'Cho phép truy cập trang dashboard và xem thống kê cơ bản.', allowedRoles: [VaiTro.QUAN_TRI_VIEN, VaiTro.NHAN_VIEN_XU_LY, VaiTro.NGUOI_XEM] },
  { id: 'MANAGE_JOBS', nhom: 'Hồ sơ', ten: 'Quản lý Hồ sơ', moTa: 'Tạo mới, chỉnh sửa và tải lên tệp tin cho hồ sơ xử lý.', allowedRoles: [VaiTro.QUAN_TRI_VIEN, VaiTro.NHAN_VIEN_XU_LY] },
  { id: 'APPROVE_JOBS', nhom: 'Hồ sơ', ten: 'Phê duyệt Hồ sơ', moTa: 'Chuyển trạng thái hồ sơ sang Sẵn sàng in hoặc Lưu trữ.', allowedRoles: [VaiTro.QUAN_TRI_VIEN] },
  { id: 'MANAGE_TEMPLATES', nhom: 'Cấu hình', ten: 'Quản lý Mẫu Hồ sơ', moTa: 'Thiết kế cấu trúc các trường dữ liệu động và OCR.', allowedRoles: [VaiTro.QUAN_TRI_VIEN, VaiTro.NHAN_VIEN_XU_LY] },
  { id: 'MANAGE_USERS', nhom: 'Quản trị', ten: 'Quản lý Tài khoản', moTa: 'Thêm, sửa, khóa và reset mật khẩu người dùng.', allowedRoles: [VaiTro.QUAN_TRI_VIEN] },
  { id: 'VIEW_LOGS', nhom: 'Quản trị', ten: 'Xem Audit Logs', moTa: 'Theo dõi lịch sử thao tác của toàn bộ thành viên.', allowedRoles: [VaiTro.QUAN_TRI_VIEN] },
  { id: 'SYSTEM_SETTINGS', nhom: 'Quản trị', ten: 'Cài đặt Hệ thống', moTa: 'Thay đổi cấu hình SMTP, Mail test và thông số lõi.', allowedRoles: [VaiTro.QUAN_TRI_VIEN] },
];

export interface SmtpSettings {
  host: string;
  port: number;
  user: string;
  pass: string;
  fromEmail: string;
  secure: boolean;
}

export interface DashboardStats {
  total: number;
  draft: number;
  ready: number;
  archived: number;
  recentActivity: HoSo[];
}

export interface SystemInfo {
  storageUsed: string;
  version: string;
  environment: string;
  lastBackup: string;
}

// Helper function for API calls
async function apiCall<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
  const user = ApiService.currentUser;
  const headers: HeadersInit = {
    'Content-Type': 'application/json',
    ...(user && {
      'x-user-id': user.id,
      'x-user-name': btoa(unescape(encodeURIComponent(user.fullName)))
    }),
    ...options.headers,
  };

  try {
    const response = await fetch(`${API_BASE_URL}${endpoint}`, {
      ...options,
      headers,
    });

    if (!response.ok) {
      const error = await response.json().catch(() => ({ error: 'Lỗi không xác định' }));
      throw new Error(error.error || `HTTP ${response.status}`);
    }

    return response.json();
  } catch (error: any) {
    // Handle network errors
    if (error.name === 'TypeError' && error.message.includes('fetch')) {
      throw new Error('Không thể kết nối đến server. Vui lòng kiểm tra backend đã chạy chưa.');
    }
    throw error;
  }
}

export class ApiService {
  // --- AUTH & SESSION ---
  static async dangNhap(username: string, pass: string) {
    const response = await apiCall<{ token: string; user: NguoiDung }>('/auth/login', {
      method: 'POST',
      body: JSON.stringify({ username, password: pass }),
    });

    localStorage.setItem(STORAGE_KEYS.USER_SESSION, JSON.stringify(response.user));
    return response;
  }

  static get currentUser(): NguoiDung | null {
    const data = localStorage.getItem(STORAGE_KEYS.USER_SESSION);
    return data ? JSON.parse(data) : null;
  }

  static async dangXuat() {
    await this.log('Đăng xuất', 'Người dùng đã thoát khỏi hệ thống');
    localStorage.removeItem(STORAGE_KEYS.USER_SESSION);
  }

  static checkPermission(actionId: string): boolean {
    const user = this.currentUser;
    if (!user) return false;
    if (user.role === VaiTro.QUAN_TRI_VIEN) return true;

    // Get permissions from API (will use default if not available)
    // For now, use default permissions check
    const perm = DEFAULT_PERMISSIONS.find(p => p.id === actionId);
    return perm ? perm.allowedRoles.includes(user.role) : false;
  }

  // --- DASHBOARD & SYSTEM ---
  static async getDashboardStats(): Promise<DashboardStats> {
    return apiCall<DashboardStats>('/dashboard/stats');
  }

  static async getSystemInfo(): Promise<SystemInfo> {
    try {
      return await apiCall<SystemInfo>('/system/info');
    } catch (error: any) {
      // Return default values if API fails
      console.warn('Failed to get system info, using defaults:', error);
      return {
        storageUsed: 'Không xác định',
        version: 'v2.5.0-enterprise',
        environment: 'Production',
        lastBackup: new Date().toLocaleDateString('vi-VN')
      };
    }
  }

  // --- USERS ---
  static async getUsers(): Promise<NguoiDung[]> {
    return apiCall<NguoiDung[]>('/users');
  }

  static async saveUser(user: NguoiDung) {
    const saved = await apiCall<NguoiDung>('/users', {
      method: 'POST',
      body: JSON.stringify(user),
    });

    // Update current user if it's the same user
    const currentUser = this.currentUser;
    if (currentUser && currentUser.id === saved.id) {
      localStorage.setItem(STORAGE_KEYS.USER_SESSION, JSON.stringify(saved));
    }

    return saved;
  }

  static async updateProfile(userId: string, fullName: string, username?: string, email?: string): Promise<NguoiDung> {
    const updated = await apiCall<NguoiDung>(`/users/${userId}/profile`, {
      method: 'PATCH',
      body: JSON.stringify({ fullName, username, email }),
    });

    // Update current user if it's the same user
    const currentUser = this.currentUser;
    if (currentUser && currentUser.id === userId) {
      localStorage.setItem(STORAGE_KEYS.USER_SESSION, JSON.stringify(updated));
    }

    return updated;
  }

  static async changePassword(userId: string, oldPassword: string, newPassword: string): Promise<void> {
    await apiCall(`/users/${userId}/change-password`, {
      method: 'PATCH',
      body: JSON.stringify({ oldPassword, newPassword }),
    });
  }

  static async toggleUserStatus(id: string) {
    await apiCall(`/users/${id}/toggle`, {
      method: 'PATCH',
    });
  }

  static async deleteUser(id: string) {
    await apiCall(`/users/${id}`, {
      method: 'DELETE',
    });
  }

  static generateStrongPassword(): string {
    const charset = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789!@#$%&*";
    return Array.from({ length: 12 }, () => charset.charAt(Math.floor(Math.random() * charset.length))).join('');
  }

  static async resetPassword(userId: string): Promise<string> {
    const response = await apiCall<{ password: string }>(`/users/${userId}/reset-password`, {
      method: 'POST',
    });
    return response.password;
  }

  // --- TEMPLATES & JOBS ---
  static async getTemplates(): Promise<MauHoSo[]> {
    return apiCall<MauHoSo[]>('/templates');
  }

  static async saveTemplate(t: MauHoSo): Promise<MauHoSo> {
    return apiCall<MauHoSo>('/templates', {
      method: 'POST',
      body: JSON.stringify(t),
    });
  }

  static async deleteTemplate(id: string) {
    await apiCall(`/templates/${id}`, {
      method: 'DELETE',
    });
  }

  static async getJobs(): Promise<HoSo[]> {
    return apiCall<HoSo[]>('/jobs');
  }

  static async getJobById(id: string): Promise<HoSo | null> {
    try {
      return await apiCall<HoSo>(`/jobs/${id}`);
    } catch (error: any) {
      if (error.message.includes('404') || error.message.includes('không tồn tại')) {
        return null;
      }
      throw error;
    }
  }

  static async saveJob(j: HoSo): Promise<HoSo> {
    return apiCall<HoSo>('/jobs', {
      method: 'POST',
      body: JSON.stringify(j),
    });
  }

  static async deleteJob(id: string) {
    await apiCall(`/jobs/${id}`, {
      method: 'DELETE',
    });
  }

  // --- INTELLIGENT OCR ---
  static async runOCR(files: File[], templateId: string): Promise<{ result: Record<string, any>, confidence: Record<string, number> }> {
    const templates = await this.getTemplates();
    const template = templates.find(t => t.id === templateId);
    if (!template) throw new Error('Cấu hình mẫu hồ sơ không hợp lệ');

    const settings: any = await this.getSettings();
    const aiConfig = settings.ai || {};
    const provider = aiConfig.provider || (import.meta as any).env.VITE_AI_PROVIDER || 'gemini';

    // 1. Pre-processing: Convert PDFs to Images
    const processedFiles: File[] = [];
    for (const file of files) {
      if (this.isPdfFile(file)) {
        // console.log(`[OCR] Converting PDF: ${file.name}`);
        const images = await PdfProcessor.toImages(file);
        processedFiles.push(...images);
      } else {
        processedFiles.push(file);
      }

    }

    // console.log(`[OCR] Processed ${processedFiles.length} images for analysis.`);

    // 2. Build Professional Prompt
    const prompt = OcrPromptEngine.build(template, processedFiles.length);

    // 3. Provider Routing
    if (provider === 'ollama') {
      return this.runOllamaOCR(processedFiles, prompt, aiConfig.ollama);
    } else if (provider === 'anything_llm') {
      return this.runAnythingLlmOCR(files, templateId); // Proxy via Backend
    } else {
      return this.runGeminiOCR(processedFiles, prompt, template, aiConfig.gemini);
    }
  }

  private static async runGeminiOCR(files: File[], prompt: string, template: MauHoSo, config: any = {}): Promise<any> {
    const apiKey = config.apiKey || (import.meta as any).env.VITE_GEMINI_API_KEY;
    if (!apiKey || apiKey === 'PLACEHOLDER_API_KEY') throw new Error('Gemini API Key missing');

    const ai = new GoogleGenAI({ apiKey });
    const modelName = config.model || (import.meta as any).env.VITE_GEMINI_MODEL || 'gemini-2.0-flash';

    const imageParts = await Promise.all(files.map(async file => ({
      inlineData: {
        data: (await this.fileToBase64(file)).split(',')[1],
        mimeType: 'image/png'
      }
    })));

    // Define strict schema for Gemini
    const schemaProps: any = {};
    const confProps: any = {};
    template.fields.forEach(f => {
      schemaProps[f.key] = { type: Type.STRING };
      confProps[f.key] = { type: Type.NUMBER };
    });

    // console.log(`[Gemini] Calling ${modelName}...`);
    // console.log('=== PROMPT SENT TO AI ===');
    // console.log(prompt.substring(0, 1000) + '...'); // Log first 1000 chars of prompt

    const response = await ai.models.generateContent({
      model: modelName,
      contents: { parts: [...imageParts, { text: prompt }] },
      config: {
        responseMimeType: "application/json",
        responseSchema: {
          type: Type.OBJECT,
          properties: {
            result: { type: Type.OBJECT, properties: schemaProps },
            confidence: { type: Type.OBJECT, properties: confProps }
          },
          required: ["result", "confidence"]
        }
      }
    });

    const responseText = response.text || '{"result":{}, "confidence":{}}';
    // console.log('=== OCR RAW RESPONSE ===');
    // console.log('Full response length:', responseText.length);
    // console.log('Full response:', responseText);
    // console.log('=== END RAW RESPONSE ===');

    let ocrResult;
    try {
      ocrResult = JSON.parse(responseText);
    } catch (parseError: any) {
      console.error('Lỗi parse JSON từ OCR response:', parseError);
      console.error('Full response:', responseText);
      // Thử extract JSON từ response nếu có text thừa
      const jsonMatch = responseText.match(/\{[\s\S]*\}/);
      if (jsonMatch) {
        ocrResult = JSON.parse(jsonMatch[0]);
      } else {
        throw new Error('Không thể parse kết quả OCR từ AI model');
      }
    }

    // Validate và log kết quả chi tiết
    // console.log('=== OCR PARSED RESULT ===');
    if (!ocrResult.result || !ocrResult.confidence) {
      console.warn('OCR result thiếu result hoặc confidence:', ocrResult);
    }

    const fieldKeys = template.fields.map(f => f.key).join(', ');
    // console.log('OCR Result keys:', Object.keys(ocrResult.result || {}));
    // console.log('Expected keys:', fieldKeys.split(', '));

    // Log từng field chi tiết
    template.fields.forEach(field => {
      const value = ocrResult.result?.[field.key] || 'NOT FOUND';
      const confidence = ocrResult.confidence?.[field.key] || 0;
      // console.log(`  ${field.label} (${field.key}): "${value}" [confidence: ${confidence}]`);
    });

    // console.log('=== END OCR RESULT ===');

    return ocrResult;
  }

  private static async runOllamaOCR(files: File[], prompt: string, config: any = {}): Promise<any> {
    const url = config.url || (import.meta as any).env.VITE_OLLAMA_URL || 'http://localhost:11434';
    const model = config.model || (import.meta as any).env.VITE_OLLAMA_MODEL || 'llama3.2-vision';
    const base64Images = await Promise.all(files.map(async f => (await this.fileToBase64(f)).split(',')[1]));

    const response = await fetch(`${url}/api/generate`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        model: model,
        prompt: prompt,
        images: base64Images,
        stream: false,
        format: 'json'
      })
    });

    if (!response.ok) throw new Error(`Ollama Error: ${response.statusText}`);
    const data = await response.json();
    return JSON.parse(data.response);
  }

  private static async runAnythingLlmOCR(files: File[], templateId: string): Promise<any> {
    const base64Files = await Promise.all(files.map(async f => ({
      name: f.name,
      content: await this.fileToBase64(f)
    })));

    return apiCall('/ocr', {
      method: 'POST',
      body: JSON.stringify({
        files: base64Files,
        templateId,
        provider: 'anything_llm'
      })
    });
  }

  static fileToBase64(file: File): Promise<string> {
    return new Promise((resolve) => {
      const reader = new FileReader();
      reader.onload = () => resolve(reader.result as string);
      reader.readAsDataURL(file);
    });
  }

  // Chuyển đổi PDF pages sang images
  static async pdfToImages(file: File): Promise<File[]> {
    return PdfProcessor.toImages(file);
  }

  // Kiểm tra xem file có phải PDF không
  static isPdfFile(file: File): boolean {
    return file.type === 'application/pdf' || file.name.toLowerCase().endsWith('.pdf');
  }

  // --- SETTINGS & PERMISSIONS ---
  static async getPermissions(): Promise<QuyenHan[]> {
    try {
      return await apiCall<QuyenHan[]>('/permissions');
    } catch {
      return DEFAULT_PERMISSIONS;
    }
  }

  static async savePermissions(p: QuyenHan[]) {
    await apiCall('/permissions', {
      method: 'POST',
      body: JSON.stringify(p),
    });
  }

  static async resetPermissions(): Promise<QuyenHan[]> {
    await this.savePermissions(DEFAULT_PERMISSIONS);
    return DEFAULT_PERMISSIONS;
  }

  static async getSettings() {
    try {
      return await apiCall('/settings');
    } catch {
      return {
        smtp: { host: 'smtp.gmail.com', port: 587, user: '', pass: '', fromEmail: 'no-reply@pro.vn', secure: true }
      };
    }
  }

  static async saveSettings(s: any) {
    await apiCall('/settings', {
      method: 'POST',
      body: JSON.stringify(s),
    });
  }

  static async testSmtp(email: string, settings: SmtpSettings): Promise<{ success: boolean; message: string; messageId?: string }> {
    return apiCall<{ success: boolean; message: string; messageId?: string }>('/settings/test-smtp', {
      method: 'POST',
      body: JSON.stringify({ email, settings }),
    });
  }

  // --- AUDIT LOGS ---
  static async log(action: string, detail: string) {
    try {
      await apiCall('/logs', {
        method: 'POST',
        body: JSON.stringify({ hanhDong: action, chiTiet: detail }),
      });
    } catch (error) {
      console.error('Failed to log:', error);
    }
  }

  static async getLogs(): Promise<NhatKy[]> {
    return apiCall<NhatKy[]>('/logs');
  }

  static async clearLogs() {
    await apiCall('/logs', {
      method: 'DELETE',
    });
    await this.log('Hệ thống', 'Làm sạch nhật ký thao tác');
  }
}
