import {
  Injectable,
  NotFoundException,
  ForbiddenException,
  Logger,
} from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import { UpdateSkillsDto } from './dto/update-skills.dto';
import { CreateExperienceDto } from './dto/create-experience.dto';
import { CreateLanguageDto } from './dto/create-language.dto';
import { CreateProjectDto } from './dto/create-project.dto';
import { UpdateProjectDto } from './dto/update-project.dto';
import { CreateCertificationDto } from './dto/create-certification.dto';
import { UpdateCertificationDto } from './dto/update-certification.dto';
import { UploadAttachmentDto } from './dto/upload-attachment.dto';

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

  constructor(private readonly prisma: PrismaService) {}

  // ============================================================
  // Profil laden / auto-erstellen
  // ============================================================
  async getOrCreateProfile(userId: string) {
    const profile = await this.prisma.expertProfile.upsert({
      where: { userId },
      create: { userId },
      update: {},
      include: {
        experiences: { orderBy: { createdAt: 'desc' } },
        languages: { orderBy: { language: 'asc' } },
        projects: { orderBy: [{ fromYear: 'desc' }, { fromMonth: 'desc' }] },
        certifications: { orderBy: { issueYear: 'desc' } },
        attachments: {
          select: { id: true, filename: true, mimetype: true, size: true, createdAt: true },
          orderBy: { createdAt: 'desc' },
        },
      },
    });

    return profile;
  }

  /**
   * Profil-ID für einen User ermitteln (erstellt bei Bedarf).
   */
  private async ensureProfileId(userId: string): Promise<string> {
    const profile = await this.prisma.expertProfile.upsert({
      where: { userId },
      create: { userId },
      update: {},
      select: { id: true },
    });
    return profile.id;
  }

  // ============================================================
  // Skills
  // ============================================================
  async updateSkills(userId: string, dto: UpdateSkillsDto) {
    const profileId = await this.ensureProfileId(userId);

    const updated = await this.prisma.expertProfile.update({
      where: { id: profileId },
      data: { skills: dto.skills },
      select: { skills: true },
    });

    this.logger.log(`Skills aktualisiert für User ${userId}: ${dto.skills.length} Skills`);
    return updated;
  }

  // ============================================================
  // Erfahrung (Experience)
  // ============================================================
  async addExperience(userId: string, dto: CreateExperienceDto) {
    const profileId = await this.ensureProfileId(userId);

    const experience = await this.prisma.expertExperience.create({
      data: {
        expertProfileId: profileId,
        area: dto.area,
        years: dto.years,
        ...(dto.level !== undefined && { level: dto.level }),
      },
    });

    this.logger.log(`Erfahrung hinzugefügt: ${dto.area} (${dto.years} Jahre)`);
    return experience;
  }

  async deleteExperience(userId: string, experienceId: string) {
    await this.verifyOwnership(userId, 'expertExperience', experienceId);

    await this.prisma.expertExperience.delete({
      where: { id: experienceId },
    });
  }

  // ============================================================
  // Sprachen (Languages)
  // ============================================================
  async addLanguage(userId: string, dto: CreateLanguageDto) {
    const profileId = await this.ensureProfileId(userId);

    const language = await this.prisma.expertLanguage.create({
      data: {
        expertProfileId: profileId,
        language: dto.language,
        level: dto.level,
      },
    });

    this.logger.log(`Sprache hinzugefügt: ${dto.language} (${dto.level})`);
    return language;
  }

  async deleteLanguage(userId: string, languageId: string) {
    await this.verifyOwnership(userId, 'expertLanguage', languageId);

    await this.prisma.expertLanguage.delete({
      where: { id: languageId },
    });
  }

  // ============================================================
  // Projekte (Projects)
  // ============================================================
  async addProject(userId: string, dto: CreateProjectDto) {
    const profileId = await this.ensureProfileId(userId);

    const project = await this.prisma.expertProject.create({
      data: {
        expertProfileId: profileId,
        fromMonth: dto.fromMonth,
        fromYear: dto.fromYear,
        toMonth: dto.isCurrent ? null : (dto.toMonth ?? null),
        toYear: dto.isCurrent ? null : (dto.toYear ?? null),
        isCurrent: dto.isCurrent ?? false,
        role: dto.role,
        ...(dto.tasks !== undefined && { tasks: dto.tasks }),
        ...(dto.company !== undefined && { company: dto.company }),
        ...(dto.companySize !== undefined && { companySize: dto.companySize }),
        ...(dto.industry !== undefined && { industry: dto.industry }),
      },
    });

    this.logger.log(`Projekt hinzugefügt: ${dto.role} (${dto.fromMonth}/${dto.fromYear})`);
    return project;
  }

  async updateProject(userId: string, projectId: string, dto: UpdateProjectDto) {
    await this.verifyOwnership(userId, 'expertProject', projectId);

    const project = await this.prisma.expertProject.update({
      where: { id: projectId },
      data: {
        ...(dto.fromMonth !== undefined && { fromMonth: dto.fromMonth }),
        ...(dto.fromYear !== undefined && { fromYear: dto.fromYear }),
        ...(dto.isCurrent !== undefined && {
          isCurrent: dto.isCurrent,
          toMonth: dto.isCurrent ? null : (dto.toMonth ?? undefined),
          toYear: dto.isCurrent ? null : (dto.toYear ?? undefined),
        }),
        ...(!dto.isCurrent && dto.toMonth !== undefined && { toMonth: dto.toMonth }),
        ...(!dto.isCurrent && dto.toYear !== undefined && { toYear: dto.toYear }),
        ...(dto.role !== undefined && { role: dto.role }),
        ...(dto.tasks !== undefined && { tasks: dto.tasks }),
        ...(dto.company !== undefined && { company: dto.company }),
        ...(dto.companySize !== undefined && { companySize: dto.companySize }),
        ...(dto.industry !== undefined && { industry: dto.industry }),
      },
    });

    return project;
  }

  async deleteProject(userId: string, projectId: string) {
    await this.verifyOwnership(userId, 'expertProject', projectId);

    await this.prisma.expertProject.delete({
      where: { id: projectId },
    });
  }

  // ============================================================
  // Zertifizierungen (Certifications)
  // ============================================================
  async addCertification(userId: string, dto: CreateCertificationDto) {
    const profileId = await this.ensureProfileId(userId);

    const certification = await this.prisma.expertCertification.create({
      data: {
        expertProfileId: profileId,
        title: dto.title,
        issuingBody: dto.issuingBody,
        ...(dto.website !== undefined && { website: dto.website }),
        issueYear: dto.issueYear,
      },
    });

    this.logger.log(`Zertifizierung hinzugefügt: ${dto.title}`);
    return certification;
  }

  async updateCertification(userId: string, certificationId: string, dto: UpdateCertificationDto) {
    await this.verifyOwnership(userId, 'expertCertification', certificationId);

    const certification = await this.prisma.expertCertification.update({
      where: { id: certificationId },
      data: {
        ...(dto.title !== undefined && { title: dto.title }),
        ...(dto.issuingBody !== undefined && { issuingBody: dto.issuingBody }),
        ...(dto.website !== undefined && { website: dto.website }),
        ...(dto.issueYear !== undefined && { issueYear: dto.issueYear }),
      },
    });

    return certification;
  }

  async deleteCertification(userId: string, certificationId: string) {
    await this.verifyOwnership(userId, 'expertCertification', certificationId);

    await this.prisma.expertCertification.delete({
      where: { id: certificationId },
    });
  }

  // ============================================================
  // Profilanlagen (Attachments)
  // ============================================================
  async uploadAttachment(userId: string, dto: UploadAttachmentDto) {
    const profileId = await this.ensureProfileId(userId);

    const attachment = await this.prisma.expertAttachment.create({
      data: {
        expertProfileId: profileId,
        filename: dto.filename,
        mimetype: dto.mimetype,
        size: dto.size,
        data: dto.data,
      },
      select: { id: true, filename: true, mimetype: true, size: true, createdAt: true },
    });

    this.logger.log(`Anhang hochgeladen: ${dto.filename} (${dto.size} Bytes)`);
    return attachment;
  }

  async downloadAttachment(userId: string, attachmentId: string) {
    const attachment = await this.prisma.expertAttachment.findUnique({
      where: { id: attachmentId },
      include: { expertProfile: { select: { userId: true } } },
    });

    if (!attachment) {
      throw new NotFoundException('Anhang nicht gefunden');
    }

    if (attachment.expertProfile.userId !== userId) {
      throw new ForbiddenException('Kein Zugriff auf diesen Anhang');
    }

    return {
      id: attachment.id,
      filename: attachment.filename,
      mimetype: attachment.mimetype,
      size: attachment.size,
      data: attachment.data,
    };
  }

  async deleteAttachment(userId: string, attachmentId: string) {
    const attachment = await this.prisma.expertAttachment.findUnique({
      where: { id: attachmentId },
      include: { expertProfile: { select: { userId: true } } },
    });

    if (!attachment) {
      throw new NotFoundException('Anhang nicht gefunden');
    }

    if (attachment.expertProfile.userId !== userId) {
      throw new ForbiddenException('Kein Zugriff auf diesen Anhang');
    }

    await this.prisma.expertAttachment.delete({
      where: { id: attachmentId },
    });

    this.logger.log(`Anhang gelöscht: ${attachment.filename}`);
  }

  // ============================================================
  // Export-Daten (User + Profil-Daten komplett)
  // ============================================================
  async getExportData(userId: string) {
    const user = await this.prisma.user.findUniqueOrThrow({
      where: { id: userId },
      select: {
        firstName: true,
        lastName: true,
        email: true,
        phone: true,
        mobile: true,
        street: true,
        postalCode: true,
        city: true,
        avatar: true,
        expertProfile: {
          include: {
            experiences: { orderBy: { createdAt: 'desc' } },
            languages: { orderBy: { language: 'asc' } },
            projects: { orderBy: [{ fromYear: 'desc' }, { fromMonth: 'desc' }] },
            certifications: { orderBy: { issueYear: 'desc' } },
            attachments: { orderBy: { createdAt: 'asc' } },
          },
        },
      },
    });

    return user;
  }

  // ============================================================
  // Hilfsfunktion: Ownership-Check
  // ============================================================
  private async verifyOwnership(
    userId: string,
    model: 'expertExperience' | 'expertLanguage' | 'expertProject' | 'expertCertification',
    entityId: string,
  ): Promise<void> {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const entity = await (this.prisma[model] as any).findUnique({
      where: { id: entityId },
      include: { expertProfile: { select: { userId: true } } },
    });

    if (!entity) {
      throw new NotFoundException('Eintrag nicht gefunden');
    }

    if (entity.expertProfile.userId !== userId) {
      throw new ForbiddenException('Kein Zugriff auf diesen Eintrag');
    }
  }
}
