import {
  Injectable,
  NotFoundException,
  ConflictException,
  Logger,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { QuestionnaireFormData } from './entities/questionnaire-form-data.entity';
import {
  CreateQuestionnaireFormDataDto,
  UpdateQuestionnaireFormDataDto,
} from './dto';
import { SmsService } from '../sms/sms.service';
import { DateIvtService } from '../date-ivt/date-ivt.service';
import { QuestionnairePartialSubmissionService } from '../questionnaire-partial-submission/questionnaire-partial-submission.service';
import { SCORE_MAP_2, SCORE_MAP_5 } from './score-map';

@Injectable()
export class QuestionnaireService {
  private readonly logger = new Logger(QuestionnaireService.name);

  constructor(
    @InjectRepository(QuestionnaireFormData)
    private readonly questionnaireRepository: Repository<QuestionnaireFormData>,
    private readonly smsService: SmsService,
    private readonly dateIvtService: DateIvtService,
    private readonly partialSubmissionService: QuestionnairePartialSubmissionService,
  ) {}

  async create(
    createQuestionnaireDto: CreateQuestionnaireFormDataDto,
  ): Promise<QuestionnaireFormData> {
    // Check if phone number already exists
    if (createQuestionnaireDto.phone_number) {
      const existingQuestionnaires = await this.findByPhoneNumber(
        createQuestionnaireDto.phone_number,
      );
      if (existingQuestionnaires.length > 0) {
        // Delete partial submissions before throwing conflict error
        try {
          const deletedCount =
            await this.partialSubmissionService.removeAllByPhoneNumber(
              createQuestionnaireDto.phone_number,
            );
          if (deletedCount > 0) {
            this.logger.log(
              `Deleted ${deletedCount} partial submission(s) for phone: ${createQuestionnaireDto.phone_number} (before conflict error)`,
            );
          }
        } catch (error) {
          // Log but don't fail if deletion fails
          this.logger.warn(
            `Failed to delete partial submission(s) for phone ${createQuestionnaireDto.phone_number} before conflict error:`,
            error instanceof Error ? error.message : String(error),
          );
        }
        
        throw new ConflictException(
          `A questionnaire with phone number ${createQuestionnaireDto.phone_number} already exists`,
        );
      }
    }

    const questionnaire = this.questionnaireRepository.create(
      createQuestionnaireDto,
    );
    const savedQuestionnaire =
      await this.questionnaireRepository.save(questionnaire);

    // Delete any partial submissions associated with this phone number
    // (handles multiple submissions in case of bugs)
    if (savedQuestionnaire.phone_number) {
      try {
        const deletedCount =
          await this.partialSubmissionService.removeAllByPhoneNumber(
            savedQuestionnaire.phone_number,
          );
        if (deletedCount > 0) {
          this.logger.log(
            `Deleted ${deletedCount} partial submission(s) for phone: ${savedQuestionnaire.phone_number}`,
          );
        }
      } catch (error) {
        // Log but don't fail if deletion fails
        this.logger.warn(
          `Failed to delete partial submission(s) for phone ${savedQuestionnaire.phone_number}:`,
          error instanceof Error ? error.message : String(error),
        );
      }
    }

    // Send SMS in background if phone number is provided
    if (savedQuestionnaire.phone_number) {
      setTimeout(() => {
        this.sendWelcomeSms(
          savedQuestionnaire.first_name,
          savedQuestionnaire.phone_number,
        );
      }, 0);
    }

    // Save IVT date in background if provided
    if (
      savedQuestionnaire.next_injection_date &&
      savedQuestionnaire.phone_number
    ) {
      setTimeout(() => {
        this.saveDateIvt({
          questionnaire_id: savedQuestionnaire.id,
          next_injection_date: savedQuestionnaire.next_injection_date,
          phone_number: savedQuestionnaire.phone_number,
          first_name: savedQuestionnaire.first_name,
        });
      }, 0);
    }

    return savedQuestionnaire;
  }

  private async sendWelcomeSms(
    firstName: string,
    phoneNumber: string,
  ): Promise<void> {
    try {
      const message = `Félicitations ${firstName}  !
Vous venez de terminer un questionnaire essentiel pour mieux adapter votre suivi.
Grace à vos réponses, nous pourrons vous envoyer régulièrement par SMS des rappels et conseils utiles et personnalisés.
Vous prenez soin de votre vue, nous sommes là pour vous accompagner ! 
Merci pour votre confiance `;

      await this.smsService.sendSms({
        to: phoneNumber,
        message: message,
        sender: 'AD+',
      });

      this.logger.log(`Welcome SMS sent successfully to ${phoneNumber}`);
    } catch (error) {
      this.logger.error(
        `Failed to send welcome SMS to ${phoneNumber}:`,
        error instanceof Error ? error.message : String(error),
      );
      // Don't throw error to avoid breaking the questionnaire creation
    }
  }

  private async saveDateIvt(data: {
    questionnaire_id: number;
    next_injection_date: Date | string;
    phone_number: string;
    first_name: string;
  }): Promise<void> {
    try {
      // Format date to ISO string (YYYY-MM-DD format)
      let injectionDate: string;
      if (data.next_injection_date instanceof Date) {
        injectionDate = data.next_injection_date.toISOString().split('T')[0];
      } else if (typeof data.next_injection_date === 'string') {
        // If it's already a string, use it directly
        injectionDate = data.next_injection_date;
      } else {
        this.logger.warn(
          `Invalid date format for questionnaire ID ${data.questionnaire_id}`,
        );
        return;
      }

      await this.dateIvtService.create({
        questionnaire_id: data.questionnaire_id,
        phone_number: data.phone_number,
        first_name: data.first_name,
        next_injection_date: injectionDate,
      });

      this.logger.log(
        `Date IVT saved successfully for questionnaire ID: ${data.questionnaire_id}`,
      );
    } catch (error) {
      this.logger.error(
        `Failed to save date IVT for questionnaire ID ${data.questionnaire_id}:`,
        error instanceof Error ? error.message : String(error),
      );
      // Don't throw error to avoid breaking the questionnaire creation
    }
  }

  async findAll(): Promise<QuestionnaireFormData[]> {
    return await this.questionnaireRepository.find({
      order: { created_at: 'DESC' },
    });
  }

  async findOne(id: number): Promise<QuestionnaireFormData> {
    const questionnaire = await this.questionnaireRepository.findOne({
      where: { id },
    });

    if (!questionnaire) {
      throw new NotFoundException(`Questionnaire with ID ${id} not found`);
    }

    return questionnaire;
  }

  async findByUuid(uuid: string): Promise<QuestionnaireFormData> {
    const questionnaire = await this.questionnaireRepository.findOne({
      where: { uuid },
    });

    if (!questionnaire) {
      throw new NotFoundException(`Questionnaire with UUID ${uuid} not found`);
    }

    return questionnaire;
  }

  async update(
    id: number,
    updateQuestionnaireDto: UpdateQuestionnaireFormDataDto,
  ): Promise<QuestionnaireFormData> {
    const questionnaire = await this.findOne(id);

    Object.assign(questionnaire, updateQuestionnaireDto);
    return await this.questionnaireRepository.save(questionnaire);
  }

  async remove(id: number): Promise<void> {
    const questionnaire = await this.findOne(id);
    await this.questionnaireRepository.remove(questionnaire);
  }

  async findByPhoneNumber(
    phoneNumber: string,
  ): Promise<QuestionnaireFormData[]> {
    return await this.questionnaireRepository.find({
      where: { phone_number: phoneNumber },
      order: { created_at: 'DESC' },
    });
  }

  async findByOphthalmologistName(
    ophthalmologistName: string,
  ): Promise<QuestionnaireFormData[]> {
    return await this.questionnaireRepository.find({
      where: { ophthalmologist_name: ophthalmologistName },
      order: { created_at: 'DESC' },
    });
  }

  async calculatePriorities(uuid: string): Promise<any> {
    const q = await this.findByUuid(uuid);

    // ---- 1. GROUPEMENTS PAR DIMENSION ----

    const GRAVITE = [
      q.dmla_threat,
      q.dmla_interference,
      q.dmla_limitation,
    ];

    const SUSCEPTIBILITE = [
      q.vision_loss_fear,
      q.treatment_need,
      q.close_follow_up_need,
    ];

    const AVANTAGES = [
      q.vision_stabilization,
      q.lesion_minimization,
      q.daily_activity_effect,
      q.follow_up_usefulness,
    ];

    const BARRIERES = [
      q.injection_pain,
      q.clinic_experience,
      q.daily_activity_interference,
      q.financial_affordability,
      q.appointment_memory,
      q.injection_problems,
      q.injection_frequency_practical,
      q.general_health_limitation,
      q.transport_problem,
      q.injection_stress,
    ];

    const INDICES_ACTION = [
      q.close_person_vision_loss,
      q.close_person_injection,
      q.ophthalmologist_encouragement,
      q.media_information,
      q.recent_vision_loss,
    ];

    // ---- 2. CALCUL SCORE ----
    const computeScore = (values: string[], scale: "5"|"2") => {
      return values.reduce((sum, v) => {
        if (!v) return sum;
        const map = scale === "5" ? SCORE_MAP_5 : SCORE_MAP_2;
        return sum + (map[v] ?? 0);
      }, 0);
    };

    const scores = {
      gravite: computeScore(GRAVITE, "5"),   // 3 questions → 3–15
      susceptibilite: computeScore(SUSCEPTIBILITE, "5"),  // 3–15
      avantages: computeScore(AVANTAGES, "5"), // 4–20
      barrieres: computeScore(BARRIERES, "5"), // inversé 10–50
      indices_action: computeScore(INDICES_ACTION, "2"), // 1–2 per question → 5–10
    };

    // ---- 3. BESOINS NORMALISÉS ----

    const besoinNormalise = {
      gravite: 1 - (scores.gravite - 3) / (15 - 3),
      susceptibilite: 1 - (scores.susceptibilite - 3) / (15 - 3),
      avantages: 1 - (scores.avantages - 4) / (20 - 4),

      barrieres: (scores.barrieres - 10) / (50 - 10), // inversé

      indices_action: 1 - (scores.indices_action - 5) / (10 - 5),
    };

    // ---- 4. PONDÉRATIONS ----

    const PRIORITES = {
      gravite: besoinNormalise.gravite * 1,
      susceptibilite: besoinNormalise.susceptibilite * 3,
      avantages: besoinNormalise.avantages * 3,
      barrieres: besoinNormalise.barrieres * 3,
      indices_action: besoinNormalise.indices_action * 1,
    };

    // ---- 5. TRI DESCENDANT ----

    const sorted = Object.entries(PRIORITES)
      .map(([key, value]) => ({ dimension: key, priorité: value }))
      .sort((a, b) => b.priorité - a.priorité);

    return {
      uuid,
      scores,
      besoinNormalise,
      priorites: PRIORITES,
      classement: sorted,
    };
  }
}
