import { Injectable, NotFoundException, BadRequestException, ConflictException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateFeedbackDto } from './dto/create-feedback.dto';
import { UpdateFeedbackDto } from './dto/update-feedback.dto';
import { Prisma } from '@prisma/client';
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';

// Custom Feedback Interface für API Response (mit Relationen)
export interface Feedback {
  id: string;
  studentId: number;
  student?: {
    id: number;
    username: string;
    email: string;
  };
  teacherId: number;
  teacher?: {
    id: number;
    username: string;
    email: string;
  };
  lessonId: number;
  timetableEntry?: {
    id: number;
    subject: {
      id: number;
      name: string;
      abbreviation?: string | null;
      color?: string | null;
    };
    teacher?: {
      id: number;
      username: string;
      email: string;
    } | null;
    room?: {
      id: number;
      number: string;
    } | null;
    dayOfWeek: number;
    startTime: string;
    endTime: string;
  };
  lessonDate: Date;
  overallRating: number;
  categories?: any;
  whatWasGood?: string | null;
  whatCanImprove?: string | null;
  additionalComments?: string | null;
  isAnonymous: boolean;
  allowTeacherResponse: boolean;
  teacherResponse?: string | null;
  teacherRespondedAt?: Date | null;
  createdAt?: Date;
  updatedAt?: Date;
}

@Injectable()
export class FeedbackService {
  constructor(private prisma: PrismaService) {}

  /**
   * Hilfsfunktion: Entfernt student-Details bei anonymen Feedbacks
   */
  private sanitizeFeedback(feedback: any): Feedback {
    if (feedback.isAnonymous) {
      const { student, studentId, ...sanitizedFeedback } = feedback;
      return this.convertToFeedback(sanitizedFeedback);
    }
    return this.convertToFeedback(feedback);
  }

  /**
   * Hilfsfunktion: Sanitiert ein Array von Feedbacks
   */
  private sanitizeFeedbacks(feedbacks: any[]): Feedback[] {
    return feedbacks.map(fb => this.sanitizeFeedback(fb));
  }

  /**
   * Konvertiert Prisma Feedback zu API Feedback
   */
  private convertToFeedback(prismaFeedback: any): Feedback {
    return {
      id: prismaFeedback.id?.toString(),
      studentId: prismaFeedback.studentId,
      student: prismaFeedback.student,
      teacherId: prismaFeedback.teacherId,
      teacher: prismaFeedback.teacher,
      lessonId: prismaFeedback.lessonId,
      timetableEntry: prismaFeedback.timetableEntry,
      lessonDate: prismaFeedback.lessonDate,
      overallRating: prismaFeedback.overallRating,
      categories: prismaFeedback.categories as any,
      whatWasGood: prismaFeedback.whatWasGood,
      whatCanImprove: prismaFeedback.whatCanImprove,
      additionalComments: prismaFeedback.additionalComments,
      isAnonymous: prismaFeedback.isAnonymous,
      allowTeacherResponse: prismaFeedback.allowTeacherResponse,
      teacherResponse: prismaFeedback.teacherResponse,
      teacherRespondedAt: prismaFeedback.teacherRespondedAt,
      createdAt: prismaFeedback.createdAt,
      updatedAt: prismaFeedback.updatedAt,
    };
  }

  async create(createFeedbackDto: CreateFeedbackDto): Promise<Feedback> {
    try {
      // Validiere Foreign Key Beziehungen
      const [student, teacher, lesson] = await Promise.all([
        this.prisma.user.findUnique({ where: { id: createFeedbackDto.studentId } }),
        this.prisma.user.findUnique({ where: { id: createFeedbackDto.teacherId } }),
        this.prisma.timetableEntry.findUnique({ where: { id: createFeedbackDto.lessonId } }),
      ]);

      if (!student) {
        throw new NotFoundException(`Schüler mit ID ${createFeedbackDto.studentId} nicht gefunden`);
      }

      if (!teacher) {
        throw new NotFoundException(`Lehrer mit ID ${createFeedbackDto.teacherId} nicht gefunden`);
      }

      if (!lesson) {
        throw new NotFoundException(`Unterrichtsstunde mit ID ${createFeedbackDto.lessonId} nicht gefunden`);
      }

      // Erstelle Feedback mit Transaction für Datenintegrität
      const feedback = await this.prisma.$transaction(async (tx) => {
        return await tx.feedback.create({
          data: {
            studentId: createFeedbackDto.studentId,
            teacherId: createFeedbackDto.teacherId,
            lessonId: createFeedbackDto.lessonId,
            lessonDate: new Date(createFeedbackDto.lessonDate),
            overallRating: createFeedbackDto.overallRating,
            categories: createFeedbackDto.categories ? JSON.parse(JSON.stringify(createFeedbackDto.categories)) : null,
            whatWasGood: createFeedbackDto.whatWasGood,
            whatCanImprove: createFeedbackDto.whatCanImprove,
            additionalComments: createFeedbackDto.additionalComments,
            isAnonymous: createFeedbackDto.isAnonymous || false,
            allowTeacherResponse: createFeedbackDto.allowTeacherResponse !== undefined
              ? createFeedbackDto.allowTeacherResponse
              : true,
          },
          include: {
            student: {
              select: {
                id: true,
                username: true,
                email: true,
              },
            },
            teacher: {
              select: {
                id: true,
                username: true,
                email: true,
              },
            },
            timetableEntry: {
              include: {
                subject: true,
                teacher: {
                  select: {
                    id: true,
                    username: true,
                    email: true,
                  },
                },
                room: {
                  select: {
                    id: true,
                    number: true,
                  },
                },
              },
            },
          },
        });
      });

      return this.sanitizeFeedback(feedback);
    } catch (error) {
      // Handle Prisma Unique Constraint Error
      if (error instanceof PrismaClientKnownRequestError) {
        if (error.code === 'P2002') {
          throw new ConflictException('Sie haben für diese Stunde bereits Feedback abgegeben');
        }
        if (error.code === 'P2003') {
          throw new BadRequestException('Ungültige Referenz: Schüler, Lehrer oder Unterrichtsstunde existiert nicht');
        }
      }
      throw error;
    }
  }

  async findAll(): Promise<Feedback[]> {
    const feedbacks = await this.prisma.feedback.findMany({
      include: {
        student: {
          select: {
            id: true,
            username: true,
            email: true,
          },
        },
        teacher: {
          select: {
            id: true,
            username: true,
            email: true,
          },
        },
        timetableEntry: {
          include: {
            subject: true,
            teacher: {
              select: {
                id: true,
                username: true,
                email: true,
              },
            },
            room: {
              select: {
                id: true,
                number: true,
              },
            },
          },
        },
      },
      orderBy: { createdAt: 'desc' },
    });
    return this.sanitizeFeedbacks(feedbacks);
  }

  async findOne(id: string): Promise<Feedback> {
    const feedback = await this.prisma.feedback.findUnique({
      where: { id: parseInt(id) },
      include: {
        student: {
          select: {
            id: true,
            username: true,
            email: true,
          },
        },
        teacher: {
          select: {
            id: true,
            username: true,
            email: true,
          },
        },
        timetableEntry: {
          include: {
            subject: true,
            teacher: {
              select: {
                id: true,
                username: true,
                email: true,
              },
            },
            room: {
              select: {
                id: true,
                number: true,
              },
            },
          },
        },
      },
    });

    if (!feedback) {
      throw new NotFoundException(`Feedback mit ID ${id} nicht gefunden`);
    }

    return this.sanitizeFeedback(feedback);
  }

  async findByTeacher(teacherId: number): Promise<Feedback[]> {
    const feedbacks = await this.prisma.feedback.findMany({
      where: { teacherId },
      include: {
        student: {
          select: {
            id: true,
            username: true,
            email: true,
          },
        },
        teacher: {
          select: {
            id: true,
            username: true,
            email: true,
          },
        },
        timetableEntry: {
          include: {
            subject: true,
            teacher: {
              select: {
                id: true,
                username: true,
                email: true,
              },
            },
            room: {
              select: {
                id: true,
                number: true,
              },
            },
          },
        },
      },
      orderBy: { createdAt: 'desc' },
    });
    return this.sanitizeFeedbacks(feedbacks);
  }

  async findByStudent(studentId: number): Promise<Feedback[]> {
    const feedbacks = await this.prisma.feedback.findMany({
      where: { studentId },
      include: {
        student: {
          select: {
            id: true,
            username: true,
            email: true,
          },
        },
        teacher: {
          select: {
            id: true,
            username: true,
            email: true,
          },
        },
        timetableEntry: {
          include: {
            subject: true,
            teacher: {
              select: {
                id: true,
                username: true,
                email: true,
              },
            },
            room: {
              select: {
                id: true,
                number: true,
              },
            },
          },
        },
      },
      orderBy: { createdAt: 'desc' },
    });
    return this.sanitizeFeedbacks(feedbacks);
  }

  async findByLesson(lessonId: number): Promise<Feedback[]> {
    const feedbacks = await this.prisma.feedback.findMany({
      where: { lessonId },
      include: {
        student: {
          select: {
            id: true,
            username: true,
            email: true,
          },
        },
        teacher: {
          select: {
            id: true,
            username: true,
            email: true,
          },
        },
        timetableEntry: {
          include: {
            subject: true,
            teacher: {
              select: {
                id: true,
                username: true,
                email: true,
              },
            },
            room: {
              select: {
                id: true,
                number: true,
              },
            },
          },
        },
      },
      orderBy: { createdAt: 'desc' },
    });
    return this.sanitizeFeedbacks(feedbacks);
  }

  async update(id: string, updateFeedbackDto: UpdateFeedbackDto): Promise<Feedback> {
    const feedbackId = parseInt(id);
    if (isNaN(feedbackId)) {
      throw new BadRequestException('Ungültige Feedback-ID');
    }

    try {
      const updatedFeedback = await this.prisma.$transaction(async (tx) => {
        const feedback = await tx.feedback.findUnique({
          where: { id: feedbackId },
        });

        if (!feedback) {
          throw new NotFoundException(`Feedback mit ID ${id} nicht gefunden`);
        }

        return await tx.feedback.update({
          where: { id: feedbackId },
          data: {
            ...updateFeedbackDto,
            categories: updateFeedbackDto.categories
              ? JSON.parse(JSON.stringify(updateFeedbackDto.categories))
              : undefined,
          },
        });
      });

      return this.sanitizeFeedback(updatedFeedback);
    } catch (error) {
      if (error instanceof NotFoundException) {
        throw error;
      }
      throw new BadRequestException('Fehler beim Aktualisieren des Feedbacks');
    }
  }

  async remove(id: string): Promise<void> {
    const feedbackId = parseInt(id);
    if (isNaN(feedbackId)) {
      throw new BadRequestException('Ungültige Feedback-ID');
    }

    try {
      await this.prisma.$transaction(async (tx) => {
        const feedback = await tx.feedback.findUnique({
          where: { id: feedbackId },
        });

        if (!feedback) {
          throw new NotFoundException(`Feedback mit ID ${id} nicht gefunden`);
        }

        await tx.feedback.delete({
          where: { id: feedbackId },
        });
      });
    } catch (error) {
      if (error instanceof NotFoundException) {
        throw error;
      }
      throw new BadRequestException('Fehler beim Löschen des Feedbacks');
    }
  }

  async addTeacherResponse(id: string, response: string): Promise<Feedback> {
    const feedbackId = parseInt(id);
    if (isNaN(feedbackId)) {
      throw new BadRequestException('Ungültige Feedback-ID');
    }

    if (!response || response.trim().length === 0) {
      throw new BadRequestException('Die Antwort darf nicht leer sein');
    }

    try {
      const updatedFeedback = await this.prisma.$transaction(async (tx) => {
        const feedback = await tx.feedback.findUnique({
          where: { id: feedbackId },
        });

        if (!feedback) {
          throw new NotFoundException(`Feedback mit ID ${id} nicht gefunden`);
        }

        if (!feedback.allowTeacherResponse) {
          throw new BadRequestException('Lehrer-Antwort ist für dieses Feedback nicht erlaubt');
        }

        return await tx.feedback.update({
          where: { id: feedbackId },
          data: {
            teacherResponse: response,
            teacherRespondedAt: new Date(),
          },
        });
      });

      return this.sanitizeFeedback(updatedFeedback);
    } catch (error) {
      if (error instanceof NotFoundException || error instanceof BadRequestException) {
        throw error;
      }
      throw new BadRequestException('Fehler beim Hinzufügen der Lehrer-Antwort');
    }
  }

  // Statistiken für Lehrer
  async getTeacherStatistics(teacherId: number) {
    const teacherFeedbacks = await this.findByTeacher(teacherId);

    if (teacherFeedbacks.length === 0) {
      return {
        totalFeedbacks: 0,
        averageRating: 0,
        categoryAverages: {
          clarity: 0,
          pace: 0,
          interaction: 0,
          materials: 0,
          atmosphere: 0,
        },
        ratingDistribution: { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 0 },
      };
    }

    // Durchschnittliche Gesamtbewertung
    const averageRating =
      teacherFeedbacks.reduce((sum, fb) => sum + fb.overallRating, 0) /
      teacherFeedbacks.length;

    // Kategorien-Durchschnitte
    const categories: {
      clarity: number[];
      pace: number[];
      interaction: number[];
      materials: number[];
      atmosphere: number[];
    } = {
      clarity: [],
      pace: [],
      interaction: [],
      materials: [],
      atmosphere: [],
    };

    teacherFeedbacks.forEach((fb) => {
      if (fb.categories) {
        if (fb.categories.clarity) categories.clarity.push(fb.categories.clarity);
        if (fb.categories.pace) categories.pace.push(fb.categories.pace);
        if (fb.categories.interaction) categories.interaction.push(fb.categories.interaction);
        if (fb.categories.materials) categories.materials.push(fb.categories.materials);
        if (fb.categories.atmosphere) categories.atmosphere.push(fb.categories.atmosphere);
      }
    });

    const categoryAverages = {
      clarity: categories.clarity.length > 0
        ? categories.clarity.reduce((a, b) => a + b, 0) / categories.clarity.length
        : 0,
      pace: categories.pace.length > 0
        ? categories.pace.reduce((a, b) => a + b, 0) / categories.pace.length
        : 0,
      interaction: categories.interaction.length > 0
        ? categories.interaction.reduce((a, b) => a + b, 0) / categories.interaction.length
        : 0,
      materials: categories.materials.length > 0
        ? categories.materials.reduce((a, b) => a + b, 0) / categories.materials.length
        : 0,
      atmosphere: categories.atmosphere.length > 0
        ? categories.atmosphere.reduce((a, b) => a + b, 0) / categories.atmosphere.length
        : 0,
    };

    // Verteilung der Bewertungen (1-10)
    const ratingDistribution = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 0 };
    teacherFeedbacks.forEach((fb) => {
      if (fb.overallRating >= 1 && fb.overallRating <= 10) {
        ratingDistribution[fb.overallRating]++;
      }
    });

    return {
      totalFeedbacks: teacherFeedbacks.length,
      averageRating: Math.round(averageRating * 10) / 10,
      categoryAverages,
      ratingDistribution,
    };
  }

  // Statistiken für eine Unterrichtsstunde
  async getLessonStatistics(lessonId: number) {
    const lessonFeedbacks = await this.findByLesson(lessonId);

    if (lessonFeedbacks.length === 0) {
      return {
        totalFeedbacks: 0,
        averageRating: 0,
        feedbacks: [],
      };
    }

    const averageRating =
      lessonFeedbacks.reduce((sum, fb) => sum + fb.overallRating, 0) /
      lessonFeedbacks.length;

    return {
      totalFeedbacks: lessonFeedbacks.length,
      averageRating: Math.round(averageRating * 10) / 10,
      feedbacks: lessonFeedbacks, // bereits sanitiert durch findByLesson
    };
  }
}
