import {
  Injectable,
  NotFoundException,
  Logger,
  ConflictException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { QuestionnairePartialSubmission } from './entities/questionnaire-partial-submission.entity';
import {
  CreateQuestionnairePartialSubmissionDto,
  UpdateQuestionnairePartialSubmissionDto,
} from './dto';

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

  constructor(
    @InjectRepository(QuestionnairePartialSubmission)
    private readonly partialSubmissionRepository: Repository<QuestionnairePartialSubmission>,
  ) {}

  async create(
    createPartialSubmissionDto: CreateQuestionnairePartialSubmissionDto,
  ): Promise<QuestionnairePartialSubmission> {
    try {
      const partialSubmission = this.partialSubmissionRepository.create(
        createPartialSubmissionDto,
      );
      const savedPartialSubmission =
        await this.partialSubmissionRepository.save(partialSubmission);

      this.logger.log(
        `Partial submission created for phone: ${savedPartialSubmission.phone_number}`,
      );
      return savedPartialSubmission;
    } catch (error: unknown) {
      if (
        error &&
        typeof error === 'object' &&
        'code' in error &&
        error.code === '23505'
      ) {
        // Unique constraint violation
        throw new ConflictException(
          'A partial submission already exists for this phone number',
        );
      }
      throw error;
    }
  }

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

  async findOne(id: number): Promise<QuestionnairePartialSubmission> {
    const partialSubmission = await this.partialSubmissionRepository.findOne({
      where: { id },
    });

    if (!partialSubmission) {
      throw new NotFoundException(`Partial submission with ID ${id} not found`);
    }

    return partialSubmission;
  }

  async findByPhoneNumber(
    phoneNumber: string,
  ): Promise<QuestionnairePartialSubmission | null> {
    return await this.partialSubmissionRepository.findOne({
      where: { phone_number: phoneNumber },
    });
  }

  async findByUuid(uuid: string): Promise<QuestionnairePartialSubmission> {
    const partialSubmission = await this.partialSubmissionRepository.findOne({
      where: { uuid },
    });

    if (!partialSubmission) {
      throw new NotFoundException(`Partial submission with UUID ${uuid} not found`);
    }

    return partialSubmission;
  }

  async update(
    id: number,
    updatePartialSubmissionDto: UpdateQuestionnairePartialSubmissionDto,
  ): Promise<QuestionnairePartialSubmission> {
    const partialSubmission = await this.findOne(id);

    Object.assign(partialSubmission, updatePartialSubmissionDto);
    const updatedPartialSubmission =
      await this.partialSubmissionRepository.save(partialSubmission);

    this.logger.log(`Partial submission updated for ID: ${id}`);
    return updatedPartialSubmission;
  }

  async updateByPhoneNumber(
    phoneNumber: string,
    updatePartialSubmissionDto: UpdateQuestionnairePartialSubmissionDto,
  ): Promise<QuestionnairePartialSubmission> {
    const partialSubmission = await this.findByPhoneNumber(phoneNumber);

    if (!partialSubmission) {
      throw new NotFoundException(
        `Partial submission for phone number ${phoneNumber} not found`,
      );
    }

    Object.assign(partialSubmission, updatePartialSubmissionDto);
    const updatedPartialSubmission =
      await this.partialSubmissionRepository.save(partialSubmission);

    this.logger.log(`Partial submission updated for phone: ${phoneNumber}`);
    return updatedPartialSubmission;
  }

  async upsert(
    createPartialSubmissionDto: CreateQuestionnairePartialSubmissionDto,
  ): Promise<QuestionnairePartialSubmission> {
    const existingSubmission = await this.findByPhoneNumber(
      createPartialSubmissionDto.phone_number,
    );

    if (existingSubmission) {
      return await this.updateByPhoneNumber(
        createPartialSubmissionDto.phone_number,
        createPartialSubmissionDto,
      );
    } else {
      return await this.create(createPartialSubmissionDto);
    }
  }

  async remove(id: number): Promise<void> {
    const partialSubmission = await this.findOne(id);
    await this.partialSubmissionRepository.remove(partialSubmission);
    this.logger.log(`Partial submission deleted for ID: ${id}`);
  }

  async removeByPhoneNumber(phoneNumber: string): Promise<void> {
    const partialSubmission = await this.findByPhoneNumber(phoneNumber);

    if (!partialSubmission) {
      throw new NotFoundException(
        `Partial submission for phone number ${phoneNumber} not found`,
      );
    }

    await this.partialSubmissionRepository.remove(partialSubmission);
    this.logger.log(`Partial submission deleted for phone: ${phoneNumber}`);
  }

  async removeAllByPhoneNumber(phoneNumber: string): Promise<number> {
    // Find all partial submissions for this phone number (in case of bugs/duplicates)
    const partialSubmissions = await this.partialSubmissionRepository.find({
      where: { phone_number: phoneNumber },
    });

    if (partialSubmissions.length === 0) {
      return 0;
    }

    await this.partialSubmissionRepository.remove(partialSubmissions);
    this.logger.log(
      `Deleted ${partialSubmissions.length} partial submission(s) for phone: ${phoneNumber}`,
    );
    return partialSubmissions.length;
  }

  async getIncompleteSubmissions(): Promise<QuestionnairePartialSubmission[]> {
    return await this.partialSubmissionRepository.find({
      where: { is_completed: false },
      order: { created_at: 'DESC' },
    });
  }

  async getCompletedSubmissions(): Promise<QuestionnairePartialSubmission[]> {
    return await this.partialSubmissionRepository.find({
      where: { is_completed: true },
      order: { created_at: 'DESC' },
    });
  }
}
