/**
 * Service de gestion Jira Cloud
 * Intégration unidirectionnelle Jira → TowerControl
 */

import { JiraTask } from '@/lib/types';
import { prisma } from '@/services/core/database';
import { parseDate, formatDateForDisplay } from '@/lib/date-utils';
import { tagsService } from '../../task-management/tags';
import { buildSyncUpdateData } from '../../task-management/readonly-fields';

export interface JiraConfig {
  enabled: boolean;
  baseUrl?: string;
  email?: string;
  apiToken?: string;
  projectKey?: string; // Clé du projet à surveiller pour les analytics d'équipe (ex: "MYTEAM")
  ignoredProjects?: string[]; // Liste des clés de projets à ignorer (ex: ["DEMO", "TEST"])
}

export interface JiraSyncAction {
  type: 'created' | 'updated' | 'skipped' | 'deleted';
  taskKey: string;
  taskTitle: string;
  reason?: string; // Raison du skip ou de la suppression
  changes?: string[]; // Liste des champs modifiés pour les updates
}

// Type générique pour compatibilité avec d'autres services
export interface SyncAction {
  type: 'created' | 'updated' | 'skipped' | 'deleted';
  itemId: string | number;
  title: string;
  message?: string;
}

export interface SyncResult {
  success: boolean;
  totalItems: number;
  actions: SyncAction[];
  errors: string[];
  unknownStatuses?: string[]; // Nouveaux statuts inconnus rencontrés
  stats: {
    created: number;
    updated: number;
    skipped: number;
    deleted: number;
  };
  // Actions originales Jira avec tous les détails (changes, etc.)
  jiraActions?: JiraSyncAction[];
}

export interface JiraSyncResult {
  success: boolean;
  tasksFound: number;
  tasksCreated: number;
  tasksUpdated: number;
  tasksSkipped: number;
  tasksDeleted: number;
  errors: string[];
  unknownStatuses?: string[]; // Statuts inconnus rencontrés
  actions: JiraSyncAction[]; // Détail des actions effectuées
}

/**
 * Interface pour les données de tâche existante
 */
interface ExistingTaskData {
  title: string;
  description: string | null;
  status: string;
  priority: string;
  dueDate: Date | null;
  jiraProject: string | null;
  jiraType: string | null;
  assignee: string | null;
}

/**
 * Détecte les changements réels et les préservations lors de la synchronisation
 * @param existingTask - Données existantes de la tâche
 * @param taskData - Données venant de Jira (avec champs Jira spécifiques)
 * @param finalData - Données finales après application de la logique de préservation (sans champs Jira)
 * @returns Objet contenant les changements réels et les champs préservés
 */
export function detectSyncChanges(
  existingTask: ExistingTaskData,
  taskData: {
    title: string;
    description: string | null;
    status: string;
    priority: string;
    dueDate: Date | null;
    jiraProject: string;
    jiraType: string;
    assignee: string | null;
  },
  finalData: {
    title: string;
    description: string | null;
    status: string;
    priority: string;
    dueDate: Date | null;
  }
): { realChanges: string[]; preservedFields: string[] } {
  const realChanges: string[] = [];
  const preservedFields: string[] = [];

  // Détecter les changements réels et les préservations
  // Pour chaque champ, comparer existingTask avec taskData pour voir les différences
  if (existingTask.title !== taskData.title) {
    if (existingTask.title !== finalData.title) {
      // Changement réel appliqué
      realChanges.push(`Titre: ${existingTask.title} → ${finalData.title}`);
    } else {
      // Valeur préservée (finalData === existingTask mais différent de taskData)
      preservedFields.push(
        `Titre: préservé localement ("${existingTask.title}") vs Jira ("${taskData.title}")`
      );
    }
  }

  if (existingTask.description !== taskData.description) {
    if (existingTask.description !== finalData.description) {
      realChanges.push(`Description modifiée`);
    }
  }

  if (existingTask.status !== taskData.status) {
    if (existingTask.status !== finalData.status) {
      realChanges.push(`Statut: ${existingTask.status} → ${finalData.status}`);
    } else {
      // Valeur préservée (finalData === existingTask mais différent de taskData)
      preservedFields.push(
        `Statut: préservé localement (${existingTask.status}) vs Jira (${taskData.status})`
      );
    }
  }

  if (existingTask.priority !== taskData.priority) {
    if (existingTask.priority !== finalData.priority) {
      realChanges.push(
        `Priorité: ${existingTask.priority} → ${finalData.priority}`
      );
    } else {
      // Valeur préservée (finalData === existingTask mais différent de taskData)
      preservedFields.push(
        `Priorité: préservée localement (${existingTask.priority}) vs Jira (${taskData.priority})`
      );
    }
  }

  if (
    (existingTask.dueDate?.getTime() || null) !==
    (finalData.dueDate?.getTime() || null)
  ) {
    const oldDate = existingTask.dueDate
      ? formatDateForDisplay(existingTask.dueDate)
      : 'Aucune';
    const newDate = finalData.dueDate
      ? formatDateForDisplay(finalData.dueDate)
      : 'Aucune';
    realChanges.push(`Échéance: ${oldDate} → ${newDate}`);
  }

  if (existingTask.jiraProject !== taskData.jiraProject) {
    realChanges.push(
      `Projet: ${existingTask.jiraProject} → ${taskData.jiraProject}`
    );
  }

  if (existingTask.jiraType !== taskData.jiraType) {
    realChanges.push(`Type: ${existingTask.jiraType} → ${taskData.jiraType}`);
  }

  if (existingTask.assignee !== taskData.assignee) {
    realChanges.push(
      `Assigné: ${existingTask.assignee} → ${taskData.assignee}`
    );
  }

  return { realChanges, preservedFields };
}

export class JiraService {
  readonly config: JiraConfig;

  constructor(config: JiraConfig) {
    this.config = config;
  }

  /**
   * Valide l'existence d'un projet Jira
   */
  async validateProject(
    projectKey: string
  ): Promise<{ exists: boolean; name?: string; error?: string }> {
    try {
      const response = await this.makeJiraRequestPrivate(
        `/rest/api/3/project/${projectKey}`
      );

      if (response.status === 404) {
        return { exists: false, error: `Projet "${projectKey}" introuvable` };
      }

      if (!response.ok) {
        const errorText = await response.text();
        return {
          exists: false,
          error: `Erreur API: ${response.status} - ${errorText}`,
        };
      }

      const project = await response.json();
      return {
        exists: true,
        name: project.name,
      };
    } catch (error) {
      console.error('Erreur lors de la validation du projet:', error);
      return {
        exists: false,
        error: error instanceof Error ? error.message : 'Erreur de connexion',
      };
    }
  }

  /**
   * Teste la connexion à Jira
   */
  async testConnection(): Promise<boolean> {
    try {
      const response = await this.makeJiraRequestPrivate('/rest/api/3/myself');
      if (!response.ok) {
        console.error(
          `Test connexion Jira échoué: ${response.status} ${response.statusText}`
        );
        const errorText = await response.text();
        console.error('Détails erreur:', errorText);
      }
      return response.ok;
    } catch (error) {
      console.error('Erreur de connexion Jira:', error);
      return false;
    }
  }

  /**
   * Valide la configuration Jira
   */
  async validateConfig(): Promise<{ valid: boolean; error?: string }> {
    if (!this.config.enabled) {
      return { valid: false, error: 'Jira désactivé' };
    }
    if (!this.config.baseUrl) {
      return { valid: false, error: 'URL de base Jira manquante' };
    }
    if (!this.config.email) {
      return { valid: false, error: 'Email Jira manquant' };
    }
    if (!this.config.apiToken) {
      return { valid: false, error: 'Token API Jira manquant' };
    }

    // Tester la connexion pour validation complète
    const connectionOk = await this.testConnection();
    if (!connectionOk) {
      return {
        valid: false,
        error: 'Impossible de se connecter avec ces paramètres',
      };
    }

    return { valid: true };
  }

  /**
   * Filtre les tâches Jira selon les projets ignorés
   */
  private filterIgnoredProjects(jiraTasks: JiraTask[]): JiraTask[] {
    if (
      !this.config.ignoredProjects ||
      this.config.ignoredProjects.length === 0
    ) {
      return jiraTasks;
    }

    const ignoredSet = new Set(
      this.config.ignoredProjects.map((p) => p.toUpperCase())
    );

    return jiraTasks.filter((task) => {
      const projectKey = task.project.key.toUpperCase();
      const shouldIgnore = ignoredSet.has(projectKey);

      if (shouldIgnore) {
        console.log(
          `🚫 Ticket ${task.key} ignoré (projet ${task.project.key} dans la liste d'exclusion)`
        );
      }

      return !shouldIgnore;
    });
  }

  /**
   * Récupère les tickets avec une requête JQL personnalisée avec pagination
   */
  async searchIssues(jql: string): Promise<JiraTask[]> {
    try {
      const fields = [
        'id',
        'key',
        'summary',
        'description',
        'status',
        'priority',
        'assignee',
        'project',
        'issuetype',
        'components',
        'fixVersions',
        'duedate',
        'created',
        'updated',
        'labels',
        'customfield_10002',
      ]; // customfield_10002 = Story Points par défaut

      const allIssues: unknown[] = [];
      let nextPageToken: string | undefined = undefined;
      let pageNumber = 1;

      console.log(
        '🔄 Récupération paginée des tickets Jira (POST /search/jql avec tokens)...'
      );

      while (true) {
        console.log(
          `📄 Page ${pageNumber} ${nextPageToken ? `(token présent)` : '(première page)'}`
        );

        // Utiliser POST /rest/api/3/search/jql avec nextPageToken selon la doc officielle
        const requestBody: {
          jql: string;
          fields: string[];
          maxResults: number;
          nextPageToken?: string;
        } = {
          jql,
          fields,
          maxResults: 50,
        };

        if (nextPageToken) {
          requestBody.nextPageToken = nextPageToken;
        }

        console.log(
          `🌐 POST /rest/api/3/search/jql avec ${nextPageToken ? 'nextPageToken' : 'première page'}`
        );

        const response = await this.makeJiraRequestPrivate(
          '/rest/api/3/search/jql',
          'POST',
          requestBody
        );

        console.log(`📡 Status réponse: ${response.status}`);

        if (!response.ok) {
          const errorText = await response.text();
          console.error(`Erreur API Jira détaillée:`, {
            status: response.status,
            statusText: response.statusText,
            url: response.url,
            errorBody: errorText,
          });
          throw new Error(
            `Erreur API Jira: ${response.status} ${response.statusText}. Détails: ${errorText.substring(0, 200)}`
          );
        }

        const data = (await response.json()) as {
          issues: unknown[];
          nextPageToken?: string;
          isLast?: boolean;
        };

        if (!data.issues || !Array.isArray(data.issues)) {
          console.error('❌ Format de données inattendu:', data);
          throw new Error(
            "Format de données Jira inattendu: pas d'array issues"
          );
        }

        allIssues.push(...data.issues);
        console.log(
          `✅ ${data.issues.length} tickets récupérés (total accumulé: ${allIssues.length})`
        );
        console.log(`🔍 Pagination info:`, {
          issuesLength: data.issues.length,
          hasNextPageToken: !!data.nextPageToken,
          isLast: data.isLast,
          pageNumber,
        });

        // Vérifier s'il y a plus de pages selon la doc officielle
        if (data.isLast === true || !data.nextPageToken) {
          console.log(
            '🏁 Dernière page atteinte (isLast=true ou pas de nextPageToken)'
          );
          break;
        }

        nextPageToken = data.nextPageToken;
        pageNumber++;

        // Sécurité: éviter les boucles infinies
        if (allIssues.length >= 10000) {
          console.warn(
            `⚠️ Limite de sécurité atteinte (${allIssues.length} tickets). Arrêt de la pagination.`
          );
          break;
        }
      }

      console.log(`🎯 Total final: ${allIssues.length} tickets Jira récupérés`);

      return allIssues.map((issue: unknown) => this.mapJiraIssueToTask(issue));
    } catch (error) {
      console.error('Erreur lors de la récupération des tickets Jira:', error);
      throw error;
    }
  }

  /**
   * Récupère les tickets assignés à l'utilisateur connecté
   * Inclut les tickets non résolus ET les tickets récemment résolus (30 derniers jours)
   * pour permettre la mise à jour du statut vers "done" lors de la synchro
   */
  async getAssignedIssues(): Promise<JiraTask[]> {
    // Récupérer les tickets non résolus + résolus récemment (30 jours)
    const jql =
      'assignee = currentUser() AND (resolution = Unresolved OR resolved >= -30d) AND issuetype != Epic ORDER BY updated DESC';
    return this.searchIssues(jql);
  }

  /**
   * S'assure que le tag "🔗 From Jira" existe dans la base
   */
  private async ensureJiraTagExists(userId: string): Promise<void> {
    try {
      const tagName = '🔗 From Jira';

      // Utiliser le service tags pour créer ou récupérer le tag
      await tagsService.ensureTagsExist([tagName], userId);
      console.log(`✅ Tag "${tagName}" créé/récupéré automatiquement`);
    } catch (error) {
      console.error('Erreur lors de la création du tag Jira:', error);
      // Ne pas faire échouer la sync pour un problème de tag
    }
  }

  /**
   * Synchronise les tickets Jira avec la base locale
   */
  async syncTasks(userId: string): Promise<SyncResult> {
    const result: SyncResult = {
      success: false,
      totalItems: 0,
      actions: [],
      errors: [],
      stats: { created: 0, updated: 0, skipped: 0, deleted: 0 },
    };

    // Variables locales pour compatibilité avec l'ancien code
    let tasksDeleted = 0;
    const jiraActions: JiraSyncAction[] = [];

    try {
      console.log('🔄 Début de la synchronisation Jira...');

      // Réinitialiser la collecte des statuts inconnus
      this.unknownStatuses.clear();

      // S'assurer que le tag "From Jira" existe
      await this.ensureJiraTagExists(userId);

      // Récupérer les tickets Jira actuellement assignés
      const jiraTasks = await this.getAssignedIssues();
      result.totalItems = jiraTasks.length;

      console.log(`📋 ${jiraTasks.length} tickets trouvés dans Jira`);

      // Filtrer les tâches selon les projets ignorés
      const filteredTasks = this.filterIgnoredProjects(jiraTasks);
      console.log(
        `🔽 ${filteredTasks.length} tickets après filtrage des projets ignorés (${jiraTasks.length - filteredTasks.length} ignorés)`
      );

      // Récupérer la liste des IDs Jira actuels pour le nettoyage (après filtrage)
      const currentJiraIds = new Set(filteredTasks.map((task) => task.id));

      // Synchroniser chaque ticket
      for (const jiraTask of filteredTasks) {
        try {
          const syncAction = await this.syncSingleTask(jiraTask, userId);

          // Convertir JiraSyncAction vers SyncAction
          const standardAction: SyncAction = {
            type: syncAction.type,
            itemId: syncAction.taskKey,
            title: syncAction.taskTitle,
            message: syncAction.reason || syncAction.changes?.join('; '),
          };

          // Ajouter l'action au résultat
          result.actions.push(standardAction);
          jiraActions.push(syncAction);

          // Compter les actions
          if (syncAction.type === 'created') {
            result.stats.created++;
          } else if (syncAction.type === 'updated') {
            result.stats.updated++;
          } else {
            result.stats.skipped++;
          }
        } catch (error) {
          console.error(`Erreur sync ticket ${jiraTask.key}:`, error);
          result.errors.push(
            `${jiraTask.key}: ${error instanceof Error ? error.message : 'Erreur inconnue'}`
          );
        }
      }

      // Nettoyer les tâches Jira qui ne sont plus assignées à l'utilisateur
      const deletedActions = await this.cleanupUnassignedTasks(currentJiraIds);
      tasksDeleted = deletedActions.length;
      result.stats.deleted = tasksDeleted;

      // Convertir les actions de suppression
      for (const action of deletedActions) {
        const standardAction: SyncAction = {
          type: 'deleted',
          itemId: action.taskKey,
          title: action.taskTitle,
          message: action.reason,
        };
        result.actions.push(standardAction);
      }

      // Ajouter les statuts inconnus au résultat
      if (this.unknownStatuses.size > 0) {
        result.unknownStatuses = Array.from(this.unknownStatuses);
      }

      // Déterminer le succès et enregistrer le log
      result.success = result.errors.length === 0;
      // Ajouter les actions Jira originales pour préserver les détails (changes, etc.)
      result.jiraActions = jiraActions;
      await this.logSync(result);

      console.log('✅ Synchronisation Jira terminée:', result);

      return result;
    } catch (error) {
      console.error('❌ Erreur générale de synchronisation:', error);
      result.errors.push(
        error instanceof Error ? error.message : 'Erreur inconnue'
      );
      result.success = false;
      await this.logSync(result);
      return result;
    }
  }

  /**
   * Synchronise un ticket Jira unique
   */
  private async syncSingleTask(
    jiraTask: JiraTask,
    userId: string
  ): Promise<JiraSyncAction> {
    // Chercher la tâche existante
    const existingTask = await prisma.task.findUnique({
      where: {
        source_sourceId: {
          source: 'jira',
          sourceId: jiraTask.id,
        },
      },
    });

    // Debug : Log du statut Jira pour comprendre le mapping
    console.log(
      `  📝 Mapping statut Jira: "${jiraTask.status.name}" → ${this.mapJiraStatusToInternal(jiraTask.status.name)}`
    );

    const taskData = {
      title: jiraTask.summary,
      description: jiraTask.description || null,
      status: this.mapJiraStatusToInternal(jiraTask.status.name),
      priority: this.mapJiraPriorityToInternal(jiraTask.priority?.name),
      source: 'jira' as const,
      sourceId: jiraTask.id,
      dueDate: jiraTask.duedate ? parseDate(jiraTask.duedate) : null,
      jiraProject: jiraTask.project.key,
      jiraKey: jiraTask.key,
      jiraType: this.mapJiraTypeToDisplay(jiraTask.issuetype.name),
      assignee: jiraTask.assignee?.displayName || null,
      updatedAt: parseDate(jiraTask.updated),
      ownerId: userId,
    };

    if (!existingTask) {
      // Préparer les données de création
      const createData: typeof taskData & {
        createdAt: Date;
        completedAt?: Date;
      } = {
        ...taskData,
        createdAt: parseDate(jiraTask.created),
      };

      // Si la tâche est déjà terminée dans Jira, définir completedAt
      if (taskData.status === 'done') {
        createData.completedAt = new Date();
        console.log(
          `  ✅ Nouvelle tâche déjà terminée dans Jira (completedAt défini)`
        );
      }

      // Créer nouvelle tâche avec le tag Jira
      const newTask = await prisma.task.create({
        data: createData,
      });

      // Assigner le tag Jira
      await this.assignJiraTag(newTask.id, userId);

      console.log(
        `➕ Nouvelle tâche créée: ${jiraTask.key} (status: ${taskData.status})`
      );
      return {
        type: 'created',
        taskKey: jiraTask.key,
        taskTitle: jiraTask.summary,
      };
    } else {
      // Toujours mettre à jour les données Jira (écrasement forcé)

      // Utiliser la logique centralisée pour déterminer quels champs préserver
      const finalData = buildSyncUpdateData('jira', existingTask, taskData);

      // Détecter les changements réels et préservations
      const { realChanges, preservedFields } = detectSyncChanges(
        existingTask,
        taskData,
        finalData
      );

      // Si tous les champs sont préservés et aucun changement réel, skip
      if (realChanges.length === 0) {
        console.log(
          `⏭️  Aucun changement pour ${jiraTask.key}, skip mise à jour`
        );

        // S'assurer que le tag Jira est assigné (pour les anciennes tâches) même en skip
        await this.assignJiraTag(existingTask.id, userId);

        return {
          type: 'skipped',
          taskKey: jiraTask.key,
          taskTitle: jiraTask.summary,
          reason: 'Tous les champs préservés localement',
          changes: preservedFields.length > 0 ? preservedFields : undefined,
        };
      }

      // Préparer les données de mise à jour en utilisant les valeurs finales déterminées par la logique centralisée
      const updateData: {
        title: string;
        description: string | null;
        status: string;
        priority: string;
        dueDate: Date | null;
        jiraProject: string;
        jiraKey: string;
        jiraType: string;
        assignee: string | null;
        updatedAt: Date;
        completedAt?: Date;
      } = {
        ...finalData,
        jiraProject: taskData.jiraProject,
        jiraKey: taskData.jiraKey,
        jiraType: taskData.jiraType,
        assignee: taskData.assignee,
        updatedAt: taskData.updatedAt,
      };

      // Si la tâche passe à "done" et n'a pas encore de completedAt, le définir
      if (finalData.status === 'done' && !existingTask.completedAt) {
        updateData.completedAt = new Date();
        console.log(`  ✅ Tâche marquée comme terminée (completedAt défini)`);
      }

      // Mettre à jour les champs Jira (titre et priorité préservés si modifiés)
      await prisma.task.update({
        where: { id: existingTask.id },
        data: updateData,
      });

      // S'assurer que le tag Jira est assigné (pour les anciennes tâches)
      await this.assignJiraTag(existingTask.id, userId);

      // Construire la liste complète des changements (réels + préservations pour info)
      const allChanges = [...realChanges, ...preservedFields];

      console.log(
        `🔄 Tâche mise à jour: ${jiraTask.key} (${realChanges.length} changement${realChanges.length > 1 ? 's' : ''} réel${realChanges.length > 1 ? 's' : ''}${preservedFields.length > 0 ? `, ${preservedFields.length} préservé${preservedFields.length > 1 ? 's' : ''}` : ''})`
      );
      return {
        type: 'updated',
        taskKey: jiraTask.key,
        taskTitle: jiraTask.summary,
        changes: allChanges,
      };
    }
  }

  /**
   * Détermine quels champs sont en lecture seule pour une tâche Jira
   * Ces champs sont ceux qui sont écrasés par la synchronisation Jira
   * @deprecated Utiliser getReadonlyFieldsForTask() depuis readonly-fields.ts à la place
   */
  async getReadonlyFields(taskId: string): Promise<string[]> {
    try {
      const task = await prisma.task.findUnique({
        where: { id: taskId },
        select: {
          source: true,
          status: true,
        },
      });

      // Si ce n'est pas une tâche Jira, aucun champ n'est en lecture seule
      if (!task || task.source !== 'jira') {
        return [];
      }

      // Utiliser la logique centralisée
      const { getReadonlyFieldsForTask } = await import(
        '../../task-management/readonly-fields'
      );
      return getReadonlyFieldsForTask(
        'jira',
        task.status as
          | 'backlog'
          | 'todo'
          | 'in_progress'
          | 'done'
          | 'cancelled'
          | 'archived'
      );
    } catch (error) {
      console.error(
        'Erreur lors de la détermination des champs readonly:',
        error
      );
      return [];
    }
  }

  /**
   * Nettoie les tâches Jira qui ne sont plus assignées à l'utilisateur
   * Ne supprime PAS les tâches déjà terminées (done/archived) pour garder l'historique
   */
  private async cleanupUnassignedTasks(
    currentJiraIds: Set<string>
  ): Promise<JiraSyncAction[]> {
    try {
      console.log('🧹 Début du nettoyage des tâches non assignées...');

      // Trouver toutes les tâches Jira existantes dans la base
      const existingJiraTasks = await prisma.task.findMany({
        where: {
          source: 'jira',
        },
        select: {
          id: true,
          sourceId: true,
          jiraKey: true,
          title: true,
          status: true,
        },
      });

      console.log(
        `📊 ${existingJiraTasks.length} tâches Jira trouvées en base`
      );

      // Identifier les tâches à supprimer (celles qui ne sont plus dans Jira)
      // MAIS on exclut les tâches déjà terminées (done/archived) car elles ont été résolues
      const tasksToDelete = existingJiraTasks.filter((task) => {
        // Si la tâche est toujours dans Jira, on la garde
        if (task.sourceId && currentJiraIds.has(task.sourceId)) {
          return false;
        }

        // Si la tâche est déjà terminée (done/archived), on la garde en base pour l'historique
        if (task.status === 'done' || task.status === 'archived') {
          console.log(
            `✅ Tâche ${task.jiraKey} terminée - conservée en base malgré absence dans Jira`
          );
          return false;
        }

        // Sinon, c'est une tâche non terminée qui n'est plus assignée → suppression
        return task.sourceId && !currentJiraIds.has(task.sourceId);
      });

      if (tasksToDelete.length === 0) {
        console.log('✅ Aucune tâche à supprimer');
        return [];
      }

      console.log(
        `🗑️ ${tasksToDelete.length} tâche(s) à supprimer (plus assignées à l'utilisateur)`
      );

      const deletedActions: JiraSyncAction[] = [];

      // Supprimer les tâches une par une avec logging
      for (const task of tasksToDelete) {
        try {
          await prisma.task.delete({
            where: { id: task.id },
          });
          console.log(`🗑️ Tâche supprimée: ${task.jiraKey} (non assignée)`);

          deletedActions.push({
            type: 'deleted',
            taskKey: task.jiraKey || 'UNKNOWN',
            taskTitle: task.title,
            reason: "Plus assignée à l'utilisateur actuel",
          });
        } catch (error) {
          console.error(`❌ Erreur suppression tâche ${task.jiraKey}:`, error);
        }
      }

      console.log(
        `✅ Nettoyage terminé: ${deletedActions.length} tâche(s) supprimée(s)`
      );
      return deletedActions;
    } catch (error) {
      console.error(
        '❌ Erreur lors du nettoyage des tâches non assignées:',
        error
      );
      // Ne pas faire échouer la sync pour un problème de nettoyage
      return [];
    }
  }

  /**
   * Assigne le tag "🔗 From Jira" à une tâche si pas déjà assigné
   */
  private async assignJiraTag(taskId: string, userId: string): Promise<void> {
    try {
      const tagName = '🔗 From Jira';

      // Utiliser le service tags pour récupérer le tag
      const jiraTag = await tagsService.getTagByName(tagName, userId);

      if (!jiraTag) {
        console.warn(`⚠️ Tag "${tagName}" introuvable lors de l'assignation`);
        return;
      }

      // Vérifier si le tag est déjà assigné
      const existingAssignment = await prisma.taskTag.findUnique({
        where: {
          taskId_tagId: {
            taskId: taskId,
            tagId: jiraTag.id,
          },
        },
      });

      if (!existingAssignment) {
        // Créer l'assignation du tag
        await prisma.taskTag.create({
          data: {
            taskId: taskId,
            tagId: jiraTag.id,
          },
        });
        console.log(`🏷️ Tag "${tagName}" assigné à la tâche`);
      }
    } catch (error) {
      console.error("Erreur lors de l'assignation du tag Jira:", error);
      // Ne pas faire échouer la sync pour un problème de tag
    }
  }

  /**
   * Mappe un issue Jira vers le format JiraTask
   */
  private mapJiraIssueToTask(issue: unknown): JiraTask {
    const issueData = issue as {
      id: string;
      key: string;
      fields: {
        summary: string;
        description?: { content?: { content?: { text: string }[] }[] };
        status: { name: string; statusCategory: { name: string } };
        priority?: { name: string };
        assignee?: { displayName: string; emailAddress: string };
        project: { key: string; name: string };
        issuetype: { name: string };
        duedate?: string;
        created: string;
        updated: string;
        labels?: string[];
        customfield_10002?: number; // Story Points field
      };
    };
    return {
      id: issueData.id,
      key: issueData.key,
      summary: issueData.fields.summary,
      description:
        issueData.fields.description?.content?.[0]?.content?.[0]?.text ||
        undefined,
      status: {
        name: issueData.fields.status.name,
        category: issueData.fields.status.statusCategory.name,
      },
      priority: issueData.fields.priority
        ? {
            name: issueData.fields.priority.name,
          }
        : undefined,
      assignee: issueData.fields.assignee
        ? {
            displayName: issueData.fields.assignee.displayName,
            emailAddress: issueData.fields.assignee.emailAddress,
          }
        : undefined,
      project: {
        key: issueData.fields.project.key,
        name: issueData.fields.project.name,
      },
      issuetype: {
        name: issueData.fields.issuetype.name,
      },
      duedate: issueData.fields.duedate,
      created: issueData.fields.created,
      updated: issueData.fields.updated,
      labels: issueData.fields.labels || [],
      storyPoints: issueData.fields.customfield_10002 || undefined,
    };
  }

  /**
   * Collecte des statuts inconnus pour le rapport
   */
  private unknownStatuses = new Set<string>();

  /**
   * Mappe les statuts Jira vers les statuts internes
   */
  private mapJiraStatusToInternal(jiraStatus: string): string {
    const statusMapping: Record<string, string> = {
      // Statuts "Backlog" (pas encore priorisés)
      Backlog: 'backlog',
      'Product backlog': 'backlog',
      'Product Discovery': 'backlog', // Phase de découverte

      // Statuts "To Do" (priorisés, prêts à développer)
      'To Do': 'todo',
      Open: 'todo',
      Ouvert: 'todo', // Français
      'Selected for Development': 'todo',
      'A faire': 'todo', // Français
      'Ready To Activate': 'todo', // Prêt à activer

      // Statuts "In Progress"
      'In Progress': 'in_progress',
      'En cours': 'in_progress', // Français
      'In Review': 'in_progress',
      'Code Review': 'in_progress',
      'Code review': 'in_progress', // Variante casse
      Testing: 'in_progress',
      'Product Delivery': 'in_progress', // Livré en prod

      // Statuts "Done"
      Done: 'done',
      DONE: 'done', // Variante majuscule
      Closed: 'done',
      CLOSED: 'done', // Variante majuscule
      Resolved: 'done',
      RESOLVED: 'done', // Variante majuscule
      Complete: 'done',
      COMPLETE: 'done', // Variante majuscule
      Finished: 'done', // Ajout
      FINISHED: 'done', // Ajout majuscule
      Livré: 'done', // Français
      LIVRÉ: 'done', // Français majuscule
      Terminé: 'done', // Français
      TERMINÉ: 'done', // Français majuscule
      Fait: 'done', // Français
      FAIT: 'done', // Français majuscule
      Clôturé: 'done', // Français - terminé/fini
      CLÔTURÉ: 'done', // Français majuscule
      Cloturé: 'done', // Sans accent
      CLOTURÉ: 'done', // Sans accent upper
      'Product Learning': 'done', // Phase d'apprentissage finale
      'PRODUCT LEARNING': 'done', // Majuscule
      'In Production': 'done', // Ajout
      'En Production': 'done', // Français

      // Statuts bloqués
      Validating: 'freeze', // Phase de validation
      Blocked: 'freeze',
      'On Hold': 'freeze',
      'En attente du support': 'freeze', // Français - bloqué en attente

      // Statuts annulés
      Cancelled: 'cancelled',
      CANCELLED: 'cancelled',
      Canceled: 'cancelled',
      CANCELED: 'cancelled',
      Annulé: 'cancelled', // Français
      ANNULÉ: 'cancelled', // Français majuscule
      Annule: 'cancelled', // Sans accent
      ANNULE: 'cancelled', // Sans accent majuscule
      Abandoned: 'cancelled',
      ABANDONED: 'cancelled',
      Abandonné: 'cancelled', // Français
      ABANDONNÉ: 'cancelled', // Français majuscule
      Abandonne: 'cancelled', // Sans accent
      ABANDONNE: 'cancelled', // Sans accent majuscule
    };

    const mapped = statusMapping[jiraStatus] || 'todo';

    // Collecter et log si statut inconnu pour debug
    if (!statusMapping[jiraStatus]) {
      this.unknownStatuses.add(jiraStatus);
      console.log(
        `⚠️ Statut Jira inconnu: "${jiraStatus}" → mappé vers "todo" par défaut`
      );
    }

    return mapped;
  }

  /**
   * Mappe les types Jira vers des termes plus courts
   */
  private mapJiraTypeToDisplay(jiraType: string): string {
    const typeMap: Record<string, string> = {
      'Nouvelle fonctionnalité': 'Feature',
      'Nouvelle Fonctionnalité': 'Feature',
      Feature: 'Feature',
      Story: 'Story',
      'User Story': 'Story',
      Tâche: 'Task',
      Task: 'Task',
      Bug: 'Bug',
      Défaut: 'Bug',
      Support: 'Support',
      Enabler: 'Enabler',
      Epic: 'Epic',
      Épique: 'Epic',
    };

    return typeMap[jiraType] || jiraType;
  }

  /**
   * Mappe les priorités Jira vers les priorités internes
   */
  private mapJiraPriorityToInternal(jiraPriority?: string): string {
    if (!jiraPriority) return 'medium';

    const priorityMapping: Record<string, string> = {
      Highest: 'urgent',
      High: 'high',
      Medium: 'medium',
      Low: 'low',
      Lowest: 'low',
    };

    return priorityMapping[jiraPriority] || 'medium';
  }

  /**
   * Effectue une requête à l'API Jira avec authentification (méthode publique pour analytics)
   */
  async makeJiraRequest(
    endpoint: string,
    method: string = 'GET',
    body?: unknown
  ): Promise<Response> {
    return this.makeJiraRequestPrivate(endpoint, method, body);
  }

  /**
   * Effectue une requête à l'API Jira avec authentification
   */
  private async makeJiraRequestPrivate(
    endpoint: string,
    method: string = 'GET',
    body?: unknown
  ): Promise<Response> {
    const url = `${this.config.baseUrl}${endpoint}`;
    const auth = Buffer.from(
      `${this.config.email}:${this.config.apiToken}`
    ).toString('base64');

    const options: RequestInit = {
      method,
      headers: {
        Authorization: `Basic ${auth}`,
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
    };

    if (body && method !== 'GET') {
      options.body = JSON.stringify(body);
    }

    return fetch(url, options);
  }

  /**
   * Enregistre un log de synchronisation
   */
  private async logSync(result: SyncResult): Promise<void> {
    try {
      await prisma.syncLog.create({
        data: {
          source: 'jira',
          status: result.success ? 'success' : 'error',
          message: result.errors.length > 0 ? result.errors.join('; ') : null,
          tasksSync: result.stats.created + result.stats.updated,
        },
      });
    } catch (error) {
      console.error("Erreur lors de l'enregistrement du log:", error);
    }
  }
}

/**
 * Factory pour créer une instance JiraService avec la config env
 */
export function createJiraService(): JiraService | null {
  const baseUrl = process.env.JIRA_BASE_URL;
  const email = process.env.JIRA_EMAIL;
  const apiToken = process.env.JIRA_API_TOKEN;

  if (!baseUrl || !email || !apiToken) {
    console.warn('Configuration Jira incomplète - service désactivé');
    return null;
  }

  return new JiraService({
    enabled: true,
    baseUrl,
    email,
    apiToken,
  });
}
