import {app, BrowserWindow, dialog, ipcMain, IpcMainInvokeEvent, Menu, nativeImage, protocol, safeStorage, shell} from 'electron';
import * as path from 'path';
import {fileURLToPath} from 'url';
import * as fs from 'fs';
import {DatabaseService, getDatabaseService} from './database/database.service.js';
import SecureStorage, {getSecureStorage} from './storage/SecureStorage.js';
import {getUserEncryptionKey, hasUserEncryptionKey, setUserEncryptionKey} from './database/keyManager.js';
import {generateUserEncryptionKey} from './database/encryption.js';
import {initAutoUpdater} from './autoUpdater.js';

// Import IPC handlers
import './ipc/book.ipc.js';
import './ipc/user.ipc.js';
import './ipc/chapter.ipc.js';
import './ipc/character.ipc.js';
import './ipc/location.ipc.js';
import './ipc/offline.ipc.js';
import './ipc/spell.ipc.js';
import './ipc/series.ipc.js';
import './ipc/series-sync.ipc.js';
import './ipc/series-character.ipc.js';
import './ipc/series-location.ipc.js';
import './ipc/series-world.ipc.js';
import './ipc/series-spell.ipc.js';
import './ipc/tombstone.ipc.js';

// Fix pour __dirname en ES modules
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const isDev = !app.isPackaged;

// Enregistrer le protocole scribedesktop:// comme standard (avant app.whenReady)
if (!isDev) {
  protocol.registerSchemesAsPrivileged([
    {
      scheme: 'scribedesktop',
      privileges: {
        standard: true,
        secure: true,
        supportFetchAPI: true,
        corsEnabled: true
      }
    }
  ]);
}

// Définir le nom de l'application
app.setName('ERitors Scribe');

// En dev et prod, __dirname pointe vers dist/electron/
const preloadPath = path.join(__dirname, 'preload.js');

// Icône de l'application
const iconPath = isDev
  ? path.join(__dirname, '../build/icon.png')
  : process.platform === 'darwin'
    ? path.join(process.resourcesPath, 'icon.icns')  // macOS utilise .icns
    : path.join(process.resourcesPath, 'app.asar/build/icon.png');  // Windows/Linux utilisent .png

let mainWindow: BrowserWindow | null = null;
let loginWindow: BrowserWindow | null = null;

function createLoginWindow(): void {
  loginWindow = new BrowserWindow({
    width: 500,
    height: 900,
    resizable: false,
    ...(process.platform !== 'darwin' && { icon: iconPath }),
    autoHideMenuBar: true,
    webPreferences: {
      preload: preloadPath,
      contextIsolation: true,
      nodeIntegration: false,
      sandbox: true,
      webSecurity: true,
      allowRunningInsecureContent: false,
      experimentalFeatures: false,
      enableBlinkFeatures: '',
      disableBlinkFeatures: '',
      webviewTag: false,
      navigateOnDragDrop: false,
    },
    frame: true,
    show: false,
  });

  if (isDev) {
    const devPort = process.env.PORT || '4000';
    loginWindow.loadURL(`http://localhost:${devPort}/login/login`);
    loginWindow.webContents.openDevTools();
  } else {
    loginWindow.loadURL('scribedesktop://./login/login/index.html');
  }

  loginWindow.once('ready-to-show', () => {
    loginWindow?.show();
    if (loginWindow) {
      initAutoUpdater(loginWindow);
    }
  });

  loginWindow.on('closed', () => {
    loginWindow = null;
  });

  // Security: Block navigation to external domains
  loginWindow.webContents.on('will-navigate', (event, navigationUrl) => {
    const parsedUrl = new URL(navigationUrl);
    if (isDev) {
      if (!parsedUrl.origin.startsWith('http://localhost')) {
        event.preventDefault();
      }
    } else {
      if (parsedUrl.protocol !== 'scribedesktop:') {
        event.preventDefault();
      }
    }
  });

  // Security: Block new window creation
  loginWindow.webContents.setWindowOpenHandler(() => {
    return { action: 'deny' };
  });
}

function createMainWindow(): void {
  mainWindow = new BrowserWindow({
    width: 1200,
    height: 800,
    ...(process.platform !== 'darwin' && { icon: iconPath }),
    autoHideMenuBar: true,
    webPreferences: {
      preload: preloadPath,
      contextIsolation: true,
      nodeIntegration: false,
      sandbox: true,
      webSecurity: true,
      allowRunningInsecureContent: false,
      experimentalFeatures: false,
      enableBlinkFeatures: '',
      disableBlinkFeatures: '',
      webviewTag: false,
      navigateOnDragDrop: false,
    },
    show: false,
  });

  if (isDev) {
    const devPort = process.env.PORT || '4000';
    mainWindow.loadURL(`http://localhost:${devPort}`);
    mainWindow.webContents.openDevTools();
  } else {
    mainWindow.loadURL('scribedesktop://./index.html');
  }

  mainWindow.once('ready-to-show', () => {
    mainWindow?.show();
    if (mainWindow) {
      initAutoUpdater(mainWindow);
    }
  });

  mainWindow.on('closed', () => {
    mainWindow = null;
  });

  // Security: Block navigation to external domains
  mainWindow.webContents.on('will-navigate', (event, navigationUrl) => {
    const parsedUrl = new URL(navigationUrl);
    if (isDev) {
      if (!parsedUrl.origin.startsWith('http://localhost')) {
        event.preventDefault();
      }
    } else {
      if (parsedUrl.protocol !== 'scribedesktop:') {
        event.preventDefault();
      }
    }
  });

  // Security: Block new window creation
  mainWindow.webContents.setWindowOpenHandler(() => {
    return { action: 'deny' };
  });
}

// IPC Handler pour ouvrir des liens externes (navigateur/app native)
ipcMain.handle('open-external', async (_event, url: string) => {
  // Security: Validate URL before opening
  try {
    const parsedUrl = new URL(url);
    const allowedProtocols = ['http:', 'https:', 'mailto:'];

    if (!allowedProtocols.includes(parsedUrl.protocol)) {
      console.error('[Security] Blocked external URL with invalid protocol:', parsedUrl.protocol);
      return;
    }

    await shell.openExternal(url);
  } catch (error) {
    console.error('[Security] Invalid URL rejected:', url);
  }
});

// IPC Handler pour OAuth login via BrowserWindow
let oauthWindow: BrowserWindow | null = null;

interface OAuthResult {
  success: boolean;
  code?: string;
  state?: string;
  error?: string;
}

interface OAuthRequest {
  provider: 'google' | 'facebook' | 'apple';
  baseUrl: string;
}

ipcMain.handle('oauth-login', async (_event, request: OAuthRequest): Promise<OAuthResult> => {
  return new Promise((resolve) => {
    const { provider, baseUrl } = request;
    const redirectUri = `${baseUrl}login?provider=${provider}`;
    const encodedRedirectUri = encodeURIComponent(redirectUri);

    // Fermer une éventuelle fenêtre OAuth existante
    if (oauthWindow) {
      oauthWindow.close();
      oauthWindow = null;
    }

    // Configuration OAuth par provider
    const oauthConfigs: Record<string, string> = {
      google: `https://accounts.google.com/o/oauth2/v2/auth?client_id=911482317931-pvjog1br22r6l8k1afq0ki94em2fsoen.apps.googleusercontent.com&redirect_uri=${encodedRedirectUri}&response_type=code&scope=openid%20email%20profile&access_type=offline`,
      facebook: `https://www.facebook.com/v18.0/dialog/oauth?client_id=1015270470233591&redirect_uri=${encodedRedirectUri}&scope=email&response_type=code&state=abc123`,
      apple: `https://appleid.apple.com/auth/authorize?client_id=eritors.apple.login&redirect_uri=${encodedRedirectUri}&response_type=code&scope=email%20name&response_mode=query&state=abc123`
    };

    const authUrl = oauthConfigs[provider];
    if (!authUrl) {
      resolve({ success: false, error: 'Invalid provider' });
      return;
    }

    oauthWindow = new BrowserWindow({
      width: 600,
      height: 700,
      show: true,
      autoHideMenuBar: true,
      webPreferences: {
        nodeIntegration: false,
        contextIsolation: true,
      }
    });

    // Intercepter les redirections pour capturer le code OAuth
    const handleNavigation = (url: string): boolean => {
      try {
        const parsedUrl = new URL(url);

        // Vérifier si c'est notre redirect URI (compare sans le query string)
        const baseRedirectUri = `${baseUrl}login`;
        if (url.startsWith(baseRedirectUri)) {
          const code = parsedUrl.searchParams.get('code');
          const state = parsedUrl.searchParams.get('state');
          const error = parsedUrl.searchParams.get('error');

          if (error) {
            resolve({ success: false, error });
          } else if (code) {
            resolve({ success: true, code, state: state || undefined });
          } else {
            resolve({ success: false, error: 'No code received' });
          }

          if (oauthWindow) {
            oauthWindow.close();
            oauthWindow = null;
          }
          return true; // Navigation interceptée
        }
      } catch (e) {
        // URL invalide, continuer
      }
      return false; // Laisser la navigation continuer
    };

    // Écouter will-redirect (redirections HTTP)
    oauthWindow.webContents.on('will-redirect', (event, url) => {
      if (handleNavigation(url)) {
        event.preventDefault();
      }
    });

    // Écouter will-navigate (navigations normales)
    oauthWindow.webContents.on('will-navigate', (event, url) => {
      if (handleNavigation(url)) {
        event.preventDefault();
      }
    });

    // Gérer la fermeture de la fenêtre par l'utilisateur
    oauthWindow.on('closed', () => {
      oauthWindow = null;
      resolve({ success: false, error: 'Window closed by user' });
    });

    // Charger l'URL OAuth
    oauthWindow.loadURL(authUrl);
  });
});

// IPC Handlers pour la gestion du token (OS-encrypted storage)
ipcMain.handle('get-token', () => {
  const storage:SecureStorage = getSecureStorage();
    return storage.get('authToken', null);
});

ipcMain.handle('set-token', (_event, token: string) => {
  const storage = getSecureStorage();
  storage.set('authToken', token);
  return true;
});

ipcMain.handle('remove-token', () => {
  const storage = getSecureStorage();
  storage.delete('authToken');
  return true;
});

// IPC Handlers pour la gestion de la langue
ipcMain.handle('get-lang', () => {
  const storage = getSecureStorage();
  return storage.get('userLang', 'fr');
});

ipcMain.handle('set-lang', (_event, lang: 'fr' | 'en') => {
  const storage = getSecureStorage();
  storage.set('userLang', lang);
  return true;
});

// IPC Handler pour initialiser l'utilisateur après récupération depuis le serveur
ipcMain.handle('init-user', async (_event:IpcMainInvokeEvent, userId: string) => {
  const storage:SecureStorage = getSecureStorage();
  storage.set('userId', userId);
  storage.set('lastUserId', userId);

  try {
    let encryptionKey: string | null;

    if (!hasUserEncryptionKey(userId)) {
      encryptionKey = generateUserEncryptionKey(userId);

      if (!encryptionKey) {
        throw new Error('Failed to generate encryption key');
      }

      setUserEncryptionKey(userId, encryptionKey);
      
      const savedKey:string = getUserEncryptionKey(userId);

      if (!savedKey) {
        throw new Error('Failed to save encryption key');
      }
    } else {
      encryptionKey = getUserEncryptionKey(userId);

      if (!encryptionKey) {
        console.error('[InitUser] CRITICAL: Existing key is undefined, regenerating');
        const { generateUserEncryptionKey } = await import('./database/encryption.js');
        encryptionKey = generateUserEncryptionKey(userId);
        setUserEncryptionKey(userId, encryptionKey);
      }
    }
    
    if (safeStorage.isEncryptionAvailable()) {
      storage.save();
    } else {
      return {
        success: false,
        error: 'Encryption is not available on this system'
      };
    }

    return { success: true, keyCreated: !hasUserEncryptionKey(userId) };
  } catch (error) {
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error'
    };
  }
});

ipcMain.on('login-success', async (_event, token: string) => {
  const storage = getSecureStorage();
  storage.set('authToken', token);

  if (loginWindow) {
    loginWindow.close();
  }

  createMainWindow();
  
  setTimeout(async ():Promise<void> => {
    try {
      if (safeStorage.isEncryptionAvailable()) {
        storage.save();
      } else {
        setTimeout(() => {
          if (safeStorage.isEncryptionAvailable()) {
            storage.save();
          } else {
            console.error('[Login] CRITICAL: Cannot encrypt credentials');
          }
        }, 1000);
      }
    } catch (error) {
      console.error('[Login] Error saving auth data:', error);
    }
  }, 500);
});

ipcMain.on('logout', ():void => {
  try {
    const storage:SecureStorage = getSecureStorage();

    storage.delete('authToken');
    storage.delete('userId');
    storage.delete('userLang');
    
    storage.save();
  } catch (error) {
    console.error('[Logout] Error clearing storage:', error);
  }

  try {
    const db:DatabaseService = getDatabaseService();
    db.close();
  } catch (error) {
    console.error('[Logout] Error closing database:', error);
  }

  if (mainWindow) {
    mainWindow.close();
    mainWindow = null;
  }

  createLoginWindow();
});

// ========== USER SYNC (PRE-AUTHENTICATION) ==========

interface SyncUserData {
  userId: string;
  firstName: string;
  lastName: string;
  username: string;
  email: string;
}

ipcMain.handle('db:user:sync', async (_event, data: SyncUserData): Promise<boolean> => {
  try {
    const { default: User } = await import('./database/models/User.js');
    const { default: UserRepo } = await import('./database/repositories/user.repository.js');

    const lang: 'fr' | 'en' = 'fr';
    try {
      UserRepo.fetchUserInfos(data.userId, lang);
      return true;
    } catch (error) {
      await User.addUser(
        data.userId,
        data.firstName,
        data.lastName,
        data.username,
        data.email,
        '',
        lang
      );
      return true;
    }
  } catch (error) {
    throw error;
  }
});

/**
 * Generate user encryption key
 */
ipcMain.handle('generate-encryption-key', async (_event, userId: string) => {
  try {
    const key:string = generateUserEncryptionKey(userId);
    return { success: true, key };
  } catch (error) {
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error'
    };
  }
});

/**
 * Get or generate user encryption key (OS-encrypted storage)
 */
ipcMain.handle('get-user-encryption-key', (_event, userId: string) => {
  const storage:SecureStorage = getSecureStorage();
  return storage.get(`encryptionKey-${userId}`, null);
});

/**
 * Store user encryption key (OS-encrypted storage)
 */
ipcMain.handle('set-user-encryption-key', (_event, userId: string, encryptionKey: string) => {
  const storage:SecureStorage = getSecureStorage();
  storage.set(`encryptionKey-${userId}`, encryptionKey);
  return true;
});

/**
 * Initialize database for user
 */
ipcMain.handle('db-initialize', (_event, userId: string, encryptionKey: string) => {
  try {
    const db:DatabaseService = getDatabaseService();
    db.initialize(userId, encryptionKey);
    return { success: true };
  } catch (error) {
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error'
    };
  }
});

/**
 * Emergency restore - Clean up ALL local data
 */
function performEmergencyRestore(): void {
  try {
    // Close database connection
    const db: DatabaseService = getDatabaseService();
    db.close();

    // Get storage and userId before clearing
    const storage: SecureStorage = getSecureStorage();
    const userId = storage.get<string>('userId');
    const lastUserId = storage.get<string>('lastUserId');

    // Delete user-specific data
    if (userId) {
      storage.delete(`pin-${userId}`);
      storage.delete(`encryptionKey-${userId}`);
    }
    if (lastUserId && lastUserId !== userId) {
      storage.delete(`pin-${lastUserId}`);
      storage.delete(`encryptionKey-${lastUserId}`);
    }

    // Delete all general data
    storage.delete('authToken');
    storage.delete('userId');
    storage.delete('lastUserId');
    storage.delete('userLang');
    storage.delete('offlineMode');
    storage.delete('syncInterval');

    // Save cleared storage
    storage.save();

    // Delete database file
    const userDataPath: string = app.getPath('userData');
    const dbPath: string = path.join(userDataPath, 'eritors-local.db');
    if (fs.existsSync(dbPath)) {
      fs.unlinkSync(dbPath);
    }

    // Delete secure config file to ensure complete reset
    const secureConfigPath: string = path.join(userDataPath, 'secure-config.json');
    if (fs.existsSync(secureConfigPath)) {
      fs.unlinkSync(secureConfigPath);
    }

    console.log('[Emergency Restore] All local data cleared successfully');
  } catch (error) {
    console.error('[Emergency Restore] Error:', error);
  }

  // Restart app
  if (mainWindow) {
    mainWindow.close();
    mainWindow = null;
  }
  if (loginWindow) {
    loginWindow.close();
    loginWindow = null;
  }

  app.relaunch();
  app.exit(0);
}

app.whenReady().then(():void => {
  // Security: Disable web cache in production
  if (!isDev) {
    app.commandLine.appendSwitch('disable-http-cache');
  }

  // Security: Set permissions request handler
  app.on('web-contents-created', (_event, contents) => {
    // Allow only clipboard permissions, block others
    contents.session.setPermissionRequestHandler((_webContents, permission, callback) => {
      const allowedPermissions: string[] = ['clipboard-read', 'clipboard-sanitized-write'];
      callback(allowedPermissions.includes(permission));
    });

    // Block all web requests to file:// protocol
    contents.session.protocol.interceptFileProtocol('file', (request, callback) => {
      callback({ error: -3 }); // net::ERR_ABORTED
    });
  });

  // Menu minimal pour garder les raccourcis DevTools et clipboard
  const template: Electron.MenuItemConstructorOptions[] = [
    {
      label: 'Edit',
      submenu: [
        { role: 'undo' },
        { role: 'redo' },
        { type: 'separator' },
        { role: 'cut' },
        { role: 'copy' },
        { role: 'paste' },
        { role: 'selectAll' }
      ]
    },
    {
      label: 'View',
      submenu: [
        { role: 'toggleDevTools' }
      ]
    },
    {
      label: 'Help',
      submenu: [
        {
          label: 'Restore App',
          click: () => {
            dialog.showMessageBox({
              type: 'warning',
              buttons: ['Cancel', 'Restore'],
              defaultId: 0,
              cancelId: 0,
              title: 'Restore App',
              message: 'Are you sure you want to restore the app?',
              detail: 'This will delete all local data including: PIN codes, encryption keys, local database, and authentication tokens. The app will restart after restoration.'
            }).then((result) => {
              if (result.response === 1) {
                performEmergencyRestore();
              }
            });
          }
        }
      ]
    }
  ];

  const menu = Menu.buildFromTemplate(template);
  Menu.setApplicationMenu(menu);

  if (!isDev) {
    const outPath:string = path.join(process.resourcesPath, 'app.asar.unpacked/out');

    protocol.handle('scribedesktop', async (request) => {
      // Security: Validate and sanitize file path
      let filePath:string = request.url.replace('scribedesktop://', '').replace(/^\.\//, '');

      // Security: Block path traversal attempts
      if (filePath.includes('..') || filePath.includes('~')) {
        console.error('[Security] Path traversal attempt blocked:', filePath);
        return new Response('Forbidden', { status: 403 });
      }

      const fullPath:string = path.normalize(path.join(outPath, filePath));

      // Security: Ensure path is within allowed directory
      if (!fullPath.startsWith(outPath)) {
        console.error('[Security] Path escape attempt blocked:', fullPath);
        return new Response('Forbidden', { status: 403 });
      }

      try {
        const data = await fs.promises.readFile(fullPath);
        const ext:string = path.extname(fullPath).toLowerCase();
        const mimeTypes: Record<string, string> = {
          '.html': 'text/html',
          '.css': 'text/css',
          '.js': 'application/javascript',
          '.json': 'application/json',
          '.png': 'image/png',
          '.jpg': 'image/jpeg',
          '.svg': 'image/svg+xml',
          '.ico': 'image/x-icon',
          '.woff': 'font/woff',
          '.woff2': 'font/woff2',
          '.ttf': 'font/ttf',
        };

        return new Response(data, {
          headers: {
            'Content-Type': mimeTypes[ext] || 'application/octet-stream',
            'X-Content-Type-Options': 'nosniff'
          }
        });
      } catch (error) {
        return new Response('Not found', { status: 404 });
      }
    });
  }

  // Définir l'icône du Dock sur macOS
  if (process.platform === 'darwin' && app.dock) {
    const icon = nativeImage.createFromPath(iconPath);
    app.dock.setIcon(icon);
  }

  // Vérifier si un token existe (OS-encrypted storage)
  const storage: SecureStorage = getSecureStorage();
  const token: string | null = storage.get('authToken');

  if (token) {
    createMainWindow();
  } else {
    createLoginWindow();
  }

  app.on('activate', ():void => {
    if (BrowserWindow.getAllWindows().length === 0) {
      const storage: SecureStorage = getSecureStorage();
      const token: string | null = storage.get('authToken');
      if (token) {
        createMainWindow();
      } else {
        createLoginWindow();
      }
    }
  });
});

app.on('window-all-closed', ():void => {
  app.quit();
});
