const express = require('express');
const multer = require('multer');
const path = require('path');
const fs = require('fs');
const { authenticate, authMiddleware } = require('./auth');
const { docker, getContainerInfo, getContainerLogs, listContainers, redeployApp, gitPull, startContainer, stopContainer, restartContainer, getServerMetrics } = require('./docker');
const { getRepo, getCommits, getBranches, listRepos } = require('./gitea');
const { checkAllApps, addDeploymentLog, updateDeploymentLog, getDeploymentLogs, getAllStatuses, getAppStatus, cleanupOrphanedDeployments } = require('./healthcheck');
const { createApplication, getAvailableStacks, initDynamicApps } = require('./app-creator');
const config = require('./config');

// Nettoyer les déploiements orphelins au démarrage (entrées "running" restantes d'un crash précédent)
cleanupOrphanedDeployments();

const router = express.Router();

// Configuration multer pour l'upload de fichiers ZIP
const uploadDir = '/tmp/dashboard-uploads';
if (!fs.existsSync(uploadDir)) {
  fs.mkdirSync(uploadDir, { recursive: true });
}

const upload = multer({
  dest: uploadDir,
  limits: { fileSize: 500 * 1024 * 1024 }, // 500MB max
  fileFilter: (req, file, cb) => {
    if (file.mimetype === 'application/zip' ||
        file.mimetype === 'application/x-zip-compressed' ||
        file.mimetype === 'application/octet-stream' ||
        file.originalname.endsWith('.zip')) {
      cb(null, true);
    } else {
      cb(new Error('Seuls les fichiers ZIP sont acceptés'), false);
    }
  },
});

// ============ AUTH ROUTES ============

router.post('/auth/login', async (req, res) => {
  try {
    const { username, password } = req.body;
    if (!username || !password) {
      return res.status(400).json({ error: 'Nom d\'utilisateur et mot de passe requis' });
    }
    const token = await authenticate(username, password);
    if (!token) {
      return res.status(401).json({ error: 'Identifiants invalides' });
    }
    // Set cookie
    res.cookie('dashboard_token', token, {
      httpOnly: true,
      secure: true,
      sameSite: 'strict',
      maxAge: 24 * 60 * 60 * 1000, // 24h
    });
    res.json({ token, username });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

router.post('/auth/logout', (req, res) => {
  res.clearCookie('dashboard_token');
  res.json({ message: 'Déconnecté' });
});

router.get('/auth/me', authMiddleware, (req, res) => {
  res.json({ username: req.user.username, role: req.user.role });
});

// ============ APPS ROUTES ============

// Liste des applications avec leur statut
router.get('/apps', authMiddleware, async (req, res) => {
  try {
    const statuses = getAllStatuses();
    if (statuses.length === 0) {
      // Première requête, lancer un check
      const results = await checkAllApps();
      return res.json(results);
    }
    res.json(statuses);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// Détail d'une application
router.get('/apps/:id', authMiddleware, async (req, res) => {
  try {
    const appConfig = config.apps.find((a) => a.id === req.params.id);
    if (!appConfig) {
      return res.status(404).json({ error: 'Application non trouvée' });
    }

    const status = getAppStatus(appConfig.id);
    const containerInfo = await getContainerInfo(appConfig.containerName);

    // Récupérer les infos Gitea
    let giteaInfo = null;
    let commits = [];
    try {
      giteaInfo = await getRepo(appConfig.giteaOwner, appConfig.giteaRepo);
      commits = await getCommits(appConfig.giteaOwner, appConfig.giteaRepo, 10);
    } catch (e) {
      // Gitea peut ne pas être disponible
    }

    res.json({
      ...status,
      container: containerInfo,
      gitea: giteaInfo
        ? {
            fullName: giteaInfo.full_name,
            description: giteaInfo.description,
            defaultBranch: giteaInfo.default_branch,
            updatedAt: giteaInfo.updated_at,
            cloneUrl: giteaInfo.clone_url,
            htmlUrl: giteaInfo.html_url,
          }
        : null,
      commits,
    });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// Forcer un health check
router.post('/apps/:id/check', authMiddleware, async (req, res) => {
  try {
    const results = await checkAllApps();
    const result = results.find((r) => r.id === req.params.id);
    res.json(result || { error: 'Application non trouvée' });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// Verrou anti-concurrence par application
const deployLocks = new Map();
const DEPLOY_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes max

// Nettoyage automatique des verrous bloqués toutes les minutes
setInterval(() => {
  const now = Date.now();
  for (const [appId, lockTime] of deployLocks.entries()) {
    if (now - lockTime > DEPLOY_TIMEOUT_MS) {
      deployLocks.delete(appId);
      const logs = getDeploymentLogs(appId);
      logs.filter(l => l.status === 'running').forEach(l => {
        l.status = 'failed';
        l.message = 'Déploiement interrompu automatiquement (timeout 10 min)';
      });
    }
  }
}, 60 * 1000);

// Redéployer une application
router.post('/apps/:id/deploy', authMiddleware, async (req, res) => {
  try {
    const appConfig = config.apps.find((a) => a.id === req.params.id);
    if (!appConfig) {
      return res.status(404).json({ error: 'Application non trouvée' });
    }

    // Vérifier si un déploiement est déjà en cours pour cette app
    if (deployLocks.has(appConfig.id)) {
      const lockAge = Math.round((Date.now() - deployLocks.get(appConfig.id)) / 1000);
      return res.status(409).json({
        error: `Un déploiement est déjà en cours pour ${appConfig.name} (depuis ${lockAge}s)`,
        status: 'running'
      });
    }

    // Poser le verrou
    deployLocks.set(appConfig.id, Date.now());

    // Ajouter un log de début et récupérer son id pour le mettre à jour plus tard
    const deployEntry = addDeploymentLog(appConfig.id, {
      status: 'running',
      message: `Déploiement de ${appConfig.name} en cours...`,
      logs: '',
      user: req.user.username,
    });

    // Lancer le redéploiement en arrière-plan
    res.json({ message: `Redéploiement de ${appConfig.name} lancé`, status: 'running' });

    // Timeout de sécurité : si le déploiement dépasse 10 min, on le marque failed
    const timeoutHandle = setTimeout(() => {
      if (deployLocks.has(appConfig.id)) {
        deployLocks.delete(appConfig.id);
        updateDeploymentLog(deployEntry.id, {
          status: 'failed',
          message: `Déploiement de ${appConfig.name} interrompu (timeout 10 min)`,
        });
      }
    }, DEPLOY_TIMEOUT_MS);

    try {
      // Exécuter le redéploiement
      const result = await redeployApp(appConfig);
      clearTimeout(timeoutHandle);

      // Mettre à jour l'entrée existante (pas de doublon "running" + "success")
      updateDeploymentLog(deployEntry.id, {
        status: result.success ? 'success' : 'failed',
        message: result.success
          ? `Redéploiement de ${appConfig.name} réussi en ${result.duration}s`
          : `Redéploiement de ${appConfig.name} échoué`,
        logs: result.logs,
        duration: result.duration,
      });

      // Relancer un health check après le déploiement
      setTimeout(() => checkAllApps(), 5000);
    } finally {
      // Toujours libérer le verrou
      deployLocks.delete(appConfig.id);
    }
  } catch (err) {
    deployLocks.delete(req.params.id);
    addDeploymentLog(req.params.id, {
      status: 'failed',
      message: `Erreur: ${err.message}`,
      logs: '',
      user: req.user.username,
    });
  }
});

// Logs d'un conteneur
router.get('/apps/:id/logs', authMiddleware, async (req, res) => {
  try {
    const appConfig = config.apps.find((a) => a.id === req.params.id);
    if (!appConfig) {
      return res.status(404).json({ error: 'Application non trouvée' });
    }
    const tail = parseInt(req.query.tail) || 100;
    const logs = await getContainerLogs(appConfig.containerName, tail);
    res.json({ logs });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// ============ CREATE APP ROUTE ============

// Récupérer les stacks disponibles
router.get('/apps-config/stacks', authMiddleware, (req, res) => {
  res.json(getAvailableStacks());
});

// Créer une nouvelle application
router.post('/apps-config/create', authMiddleware, upload.single('zipFile'), async (req, res) => {
  try {
    const { name, description, subdomain, stack, port, needsDb, dbType, sourceType, giteaRepoUrl } = req.body;

    // Validation
    if (!name || !subdomain || !stack || !sourceType) {
      return res.status(400).json({
        error: 'Champs requis manquants: name, subdomain, stack, sourceType',
      });
    }

    // Vérifier que le subdomain est valide
    const subdomainRegex = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
    if (!subdomainRegex.test(subdomain)) {
      return res.status(400).json({
        error: 'Le sous-domaine ne peut contenir que des lettres minuscules, chiffres et tirets',
      });
    }

    // Vérifier que l'app n'existe pas déjà
    const existingApp = config.apps.find((a) => a.id === subdomain);
    if (existingApp) {
      return res.status(409).json({
        error: `Une application avec l'identifiant "${subdomain}" existe déjà`,
      });
    }

    // Vérifier le fichier ZIP si sourceType === 'zip'
    if (sourceType === 'zip' && !req.file) {
      return res.status(400).json({
        error: 'Un fichier ZIP est requis pour le type de source "zip"',
      });
    }

    // Vérifier l'URL Gitea si sourceType === 'gitea'
    if (sourceType === 'gitea' && !giteaRepoUrl) {
      return res.status(400).json({
        error: 'L\'URL du dépôt Gitea est requise pour le type de source "gitea"',
      });
    }

    // Log de début de création et récupérer l'id pour mise à jour ultérieure
    const logLines = [];
    const createEntry = addDeploymentLog(subdomain, {
      status: 'running',
      message: `Création de l'application ${name} en cours...`,
      logs: '',
      user: req.user.username,
    });

    // Répondre immédiatement
    res.json({
      message: `Création de l'application ${name} lancée`,
      status: 'running',
      appId: subdomain,
    });

    // Lancer la création en arrière-plan
    try {
      const result = await createApplication(
        {
          name,
          description,
          subdomain,
          stack,
          port: port ? parseInt(port) : null,
          needsDb: needsDb === 'true' || needsDb === true,
          dbType: dbType || 'mysql',
          sourceType,
          giteaRepoUrl,
          zipFilePath: req.file ? req.file.path : null,
        },
        (line) => logLines.push(line)
      );

      updateDeploymentLog(createEntry.id, {
        status: 'success',
        message: `Application ${name} créée et déployée avec succès`,
        logs: logLines.join('\n'),
      });

      // Relancer un health check
      setTimeout(() => checkAllApps(), 10000);
    } catch (createErr) {
      updateDeploymentLog(createEntry.id, {
        status: 'failed',
        message: `Création de ${name} échouée: ${createErr.message}`,
        logs: logLines.join('\n'),
      });
    }
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// ============ DEPLOYMENT LOGS ============

router.get('/deployments', authMiddleware, (req, res) => {
  const appId = req.query.appId || null;
  res.json(getDeploymentLogs(appId));
});

// ============ GITEA ROUTES ============

router.get('/gitea/repos', authMiddleware, async (req, res) => {
  try {
    const repos = await listRepos();
    res.json(
      repos.map((r) => ({
        id: r.id,
        name: r.name,
        fullName: r.full_name,
        description: r.description,
        private: r.private,
        htmlUrl: r.html_url,
        cloneUrl: r.clone_url,
        updatedAt: r.updated_at,
        language: r.language,
        size: r.size,
      }))
    );
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

router.get('/gitea/repos/:owner/:repo/commits', authMiddleware, async (req, res) => {
  try {
    const commits = await getCommits(req.params.owner, req.params.repo, 20);
    res.json(commits);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

router.get('/gitea/repos/:owner/:repo/branches', authMiddleware, async (req, res) => {
  try {
    const branches = await getBranches(req.params.owner, req.params.repo);
    res.json(branches);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// ============ DOCKER ROUTES ============

router.get('/docker/containers', authMiddleware, async (req, res) => {
  try {
    const containers = await listContainers();
    res.json(containers);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// ============ SYSTEM INFO ============

router.get('/system/info', authMiddleware, async (req, res) => {
  try {
    const containers = await listContainers();
    res.json({
      totalApps: config.apps.length,
      totalContainers: containers.length,
      runningContainers: containers.filter((c) => c.state === 'running').length,
      giteaUrl: config.gitea.url,
      serverTime: new Date().toISOString(),
    });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});


// ============ CONTAINER CONTROL (start/stop/restart) ============
router.post('/apps/:id/start', authMiddleware, async (req, res) => {
  try {
    const app = config.apps.find((a) => a.id === req.params.id);
    if (!app) return res.status(404).json({ error: 'Application non trouvée' });
    const containerName = app.containerName || app.id;
    const result = await startContainer(containerName);
    res.json(result);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

router.post('/apps/:id/stop', authMiddleware, async (req, res) => {
  try {
    const app = config.apps.find((a) => a.id === req.params.id);
    if (!app) return res.status(404).json({ error: 'Application non trouvée' });
    const containerName = app.containerName || app.id;
    const result = await stopContainer(containerName);
    res.json(result);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

router.post('/apps/:id/restart', authMiddleware, async (req, res) => {
  try {
    const app = config.apps.find((a) => a.id === req.params.id);
    if (!app) return res.status(404).json({ error: 'Application non trouvée' });
    const containerName = app.containerName || app.id;
    const result = await restartContainer(containerName);
    res.json(result);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// ============ SERVER METRICS ============
router.get('/system/metrics', authMiddleware, async (req, res) => {
  try {
    const metrics = await getServerMetrics();
    res.json(metrics);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

module.exports = router;

// ===== INVENTAIRE DES APPLICATIONS =====
const { exec } = require('child_process');
const GITEA_RECETTE_INTERNAL = process.env.GITEA_RECETTE_URL || 'http://gitea:3000';
const GITEA_RECETTE_PUBLIC = process.env.GITEA_RECETTE_PUBLIC_URL || 'https://git.recette.santinova-soft.org';
const GITEA_PROD_EXTERNAL = 'https://git.santinova-soft.org';
const GITEA_USER_INV = process.env.GITEA_USERNAME || 'manus-admin';
const GITEA_PASS_REC = process.env.GITEA_PASSWORD || 'Itinova69!';
const GITEA_PASS_PRD = process.env.GITEA_PASSWORD_PROD || 'ManusGitea2026!';

const INVENTORY_APPS = [
  { id: 'itinova-contacts', name: 'Itinova Contacts', repoName: 'itinova-contacts' },
  { id: 'itinova-podcasts', name: 'Itinova Podcasts', repoName: 'itinova-podcasts' },
  { id: 'veille-reglementaire', name: 'Veille Réglementaire', repoName: 'veille-reglementaire' },
  { id: 'itinova-vehicle-exchange', name: 'Itinova Gestion de Flotte', repoName: 'itinova-vehicle-exchange' },
  { id: 'sonum', name: 'SONUM', repoName: 'sonum' },
  { id: 'demat-facturation-dsi', name: 'Démat. Facturation DSI', repoName: 'demat-facturation-dsi' },
  { id: 'facturation-santinova', name: 'Facturation Santinova', repoName: 'facturation-santinova' },
  { id: 'formation-manager-itinova', name: 'Formation Manager', repoName: 'formation-manager-itinova' },
  { id: 'portail-santinova', name: 'Portail Applicatif', repoName: 'portail-santinova' },
  { id: 'manus-dashboard', name: 'Dashboard Manus', repoName: 'manus-dashboard' },
];

function curlGitea(baseUrl, owner, repo, pass, publicBaseUrl) {
  return new Promise((resolve) => {
    const auth = `${GITEA_USER_INV}:${pass}`;
    const cmd = `curl -sk --max-time 6 -u "${auth}" "${baseUrl}/api/v1/repos/${owner}/${repo}"`;
    exec(cmd, { timeout: 7000 }, (err, stdout) => {
      if (err || !stdout) return resolve({ present: false, url: null, version: null });
      try {
        const data = JSON.parse(stdout);
        if (!data.id) return resolve({ present: false, url: null, version: null });
        // Récupérer le dernier commit
        const cmd2 = `curl -sk --max-time 6 -u "${auth}" "${baseUrl}/api/v1/repos/${owner}/${repo}/commits?limit=1"`;
        exec(cmd2, { timeout: 7000 }, (err2, stdout2) => {
          let version = null;
          try {
            const commits = JSON.parse(stdout2 || '[]');
            if (commits.length > 0) {
              const c = commits[0];
              const sha = c.sha ? c.sha.substring(0, 7) : null;
              const date = c.commit && c.commit.author && c.commit.author.date
                ? new Date(c.commit.author.date).toLocaleDateString('fr-FR') : null;
              version = sha && date ? `${sha} (${date})` : sha;
            }
          } catch(e) {}
          const displayUrl = publicBaseUrl || baseUrl;
          resolve({ present: true, url: `${displayUrl}/${owner}/${repo}`, version });
        });
      } catch(e) {
        resolve({ present: false, url: null, version: null });
      }
    });
  });
}

router.get('/inventory', authMiddleware, async (req, res) => {
  try {
    const owner = GITEA_USER_INV;
    const results = await Promise.all(
      INVENTORY_APPS.map(async (app) => {
        const [repoRecette, repoProd] = await Promise.all([
          curlGitea(GITEA_RECETTE_INTERNAL, owner, app.repoName, GITEA_PASS_REC, GITEA_RECETTE_PUBLIC),
          curlGitea(GITEA_PROD_EXTERNAL, owner, app.repoName, GITEA_PASS_PRD),
        ]);
        return { ...app, repoRecette, repoProd };
      })
    );
    res.json(results);
  } catch (err) {
    console.error('Erreur /inventory:', err.message);
    res.status(500).json({ error: err.message });
  }
});
;
