import {
  Injectable,
  NotFoundException,
  ConflictException,
  BadRequestException,
} from '@nestjs/common';
import { PrismaService } from '../../../prisma/prisma.service';
import {
  CreateMeetingDto,
  UpdateMeetingDto,
  QueryMeetingsDto,
  CreateTopicDto,
  UpdateTopicDto,
  CreateActionDto,
  UpdateActionDto,
} from './dto';
import { Prisma, MeetingStatus, ActionStatus, SQCDMCategory } from '@prisma/client';

/**
 * Default KPI templates for each SQCDM category
 * These are used when creating a new meeting to pre-populate topics
 */
const DEFAULT_TOPIC_TEMPLATES: Array<{
  category: SQCDMCategory;
  title: string;
  unit?: string;
}> = [
  { category: 'SAFETY', title: 'Arbeitsunfaelle', unit: 'Anzahl' },
  { category: 'SAFETY', title: 'Beinahe-Unfaelle', unit: 'Anzahl' },
  { category: 'QUALITY', title: 'Fehlerquote', unit: '%' },
  { category: 'QUALITY', title: 'Reklamationen', unit: 'Anzahl' },
  { category: 'COST', title: 'Ausschuss', unit: '%' },
  { category: 'COST', title: 'Nacharbeit', unit: 'Stunden' },
  { category: 'DELIVERY', title: 'Liefertreue', unit: '%' },
  { category: 'DELIVERY', title: 'Durchlaufzeit', unit: 'Tage' },
  { category: 'MORALE', title: 'Anwesenheit', unit: '%' },
  { category: 'MORALE', title: 'Verbesserungsvorschlaege', unit: 'Anzahl' },
];

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

  // ============================================================================
  // MEETING OPERATIONS
  // ============================================================================

  /**
   * Create a new morning meeting
   */
  async createMeeting(createDto: CreateMeetingDto) {
    // Validate department exists
    const department = await this.prisma.department.findUnique({
      where: { id: createDto.departmentId },
    });

    if (!department) {
      throw new NotFoundException(
        `Department with ID ${createDto.departmentId} not found`,
      );
    }

    // Check if meeting already exists for this date and department
    const existingMeeting = await this.prisma.morningMeeting.findUnique({
      where: {
        departmentId_date: {
          departmentId: createDto.departmentId,
          date: new Date(createDto.date),
        },
      },
    });

    if (existingMeeting) {
      throw new ConflictException(
        `Meeting already exists for department ${department.name} on ${createDto.date}`,
      );
    }

    // Validate conductor if provided
    if (createDto.conductorId) {
      const conductor = await this.prisma.user.findUnique({
        where: { id: createDto.conductorId },
      });
      if (!conductor) {
        throw new NotFoundException(
          `Conductor with ID ${createDto.conductorId} not found`,
        );
      }
    }

    // Create meeting with default topics
    return this.prisma.morningMeeting.create({
      data: {
        departmentId: createDto.departmentId,
        date: new Date(createDto.date),
        conductorId: createDto.conductorId,
        participants: createDto.participants || [],
        notes: createDto.notes,
        status: createDto.status || 'SCHEDULED',
        topics: {
          create: DEFAULT_TOPIC_TEMPLATES.map((template, index) => ({
            category: template.category,
            title: template.title,
            unit: template.unit,
            sortOrder: index,
          })),
        },
      },
      include: this.getMeetingIncludes(),
    });
  }

  /**
   * Get all meetings with pagination and filters
   */
  async findAllMeetings(query: QueryMeetingsDto) {
    const {
      page = 1,
      limit = 20,
      departmentId,
      status,
      dateFrom,
      dateTo,
      conductorId,
      sortBy = 'date',
      sortOrder = 'desc',
    } = query;
    const skip = (page - 1) * limit;

    const where: Prisma.MorningMeetingWhereInput = {
      ...(departmentId && { departmentId }),
      ...(status && { status }),
      ...(conductorId && { conductorId }),
      ...(dateFrom || dateTo
        ? {
            date: {
              ...(dateFrom && { gte: new Date(dateFrom) }),
              ...(dateTo && { lte: new Date(dateTo) }),
            },
          }
        : {}),
    };

    const orderBy: Prisma.MorningMeetingOrderByWithRelationInput = {
      [sortBy]: sortOrder,
    };

    const [meetings, total] = await Promise.all([
      this.prisma.morningMeeting.findMany({
        where,
        skip,
        take: limit,
        orderBy,
        include: this.getMeetingIncludes(),
      }),
      this.prisma.morningMeeting.count({ where }),
    ]);

    return {
      data: meetings,
      meta: {
        total,
        page,
        limit,
        totalPages: Math.ceil(total / limit),
      },
    };
  }

  /**
   * Get a single meeting by ID
   */
  async findMeetingById(id: string) {
    const meeting = await this.prisma.morningMeeting.findUnique({
      where: { id },
      include: this.getMeetingIncludes(),
    });

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

    return meeting;
  }

  /**
   * Get today's meeting for a department (or create if not exists)
   */
  async getTodaysMeeting(departmentId: string, createIfNotExists = false) {
    const today = new Date();
    today.setHours(0, 0, 0, 0);

    let meeting = await this.prisma.morningMeeting.findUnique({
      where: {
        departmentId_date: {
          departmentId,
          date: today,
        },
      },
      include: this.getMeetingIncludes(),
    });

    if (!meeting && createIfNotExists) {
      meeting = await this.createMeeting({
        departmentId,
        date: today.toISOString().split('T')[0],
      });
    }

    if (!meeting) {
      throw new NotFoundException(
        `No meeting found for today in department ${departmentId}`,
      );
    }

    return meeting;
  }

  /**
   * Update a meeting
   */
  async updateMeeting(id: string, updateDto: UpdateMeetingDto) {
    await this.findMeetingById(id);

    // Validate conductor if changing
    if (updateDto.conductorId) {
      const conductor = await this.prisma.user.findUnique({
        where: { id: updateDto.conductorId },
      });
      if (!conductor) {
        throw new NotFoundException(
          `Conductor with ID ${updateDto.conductorId} not found`,
        );
      }
    }

    return this.prisma.morningMeeting.update({
      where: { id },
      data: {
        ...(updateDto.conductorId !== undefined && {
          conductorId: updateDto.conductorId,
        }),
        ...(updateDto.participants !== undefined && {
          participants: updateDto.participants,
        }),
        ...(updateDto.notes !== undefined && { notes: updateDto.notes }),
        ...(updateDto.status !== undefined && { status: updateDto.status }),
        ...(updateDto.startTime && { startTime: new Date(updateDto.startTime) }),
        ...(updateDto.endTime && { endTime: new Date(updateDto.endTime) }),
        ...(updateDto.duration !== undefined && { duration: updateDto.duration }),
      },
      include: this.getMeetingIncludes(),
    });
  }

  /**
   * Delete a meeting
   */
  async deleteMeeting(id: string) {
    await this.findMeetingById(id);

    return this.prisma.morningMeeting.delete({
      where: { id },
      include: this.getMeetingIncludes(),
    });
  }

  /**
   * Start a meeting (set status to IN_PROGRESS and record start time)
   */
  async startMeeting(id: string) {
    const meeting = await this.findMeetingById(id);

    if (meeting.status !== 'SCHEDULED') {
      throw new BadRequestException(
        `Cannot start meeting with status ${meeting.status}`,
      );
    }

    return this.prisma.morningMeeting.update({
      where: { id },
      data: {
        status: 'IN_PROGRESS',
        startTime: new Date(),
      },
      include: this.getMeetingIncludes(),
    });
  }

  /**
   * End a meeting (set status to COMPLETED and record end time/duration)
   */
  async endMeeting(id: string) {
    const meeting = await this.findMeetingById(id);

    if (meeting.status !== 'IN_PROGRESS') {
      throw new BadRequestException(
        `Cannot end meeting with status ${meeting.status}`,
      );
    }

    const endTime = new Date();
    const duration = meeting.startTime
      ? Math.round((endTime.getTime() - meeting.startTime.getTime()) / 60000)
      : null;

    return this.prisma.morningMeeting.update({
      where: { id },
      data: {
        status: 'COMPLETED',
        endTime,
        duration,
      },
      include: this.getMeetingIncludes(),
    });
  }

  /**
   * Get meeting history for a department
   */
  async getMeetingHistory(departmentId: string, days = 30) {
    const startDate = new Date();
    startDate.setDate(startDate.getDate() - days);

    return this.prisma.morningMeeting.findMany({
      where: {
        departmentId,
        date: { gte: startDate },
      },
      orderBy: { date: 'desc' },
      include: this.getMeetingIncludes(),
    });
  }

  // ============================================================================
  // TOPIC OPERATIONS
  // ============================================================================

  /**
   * Add a topic to a meeting
   */
  async addTopic(meetingId: string, createDto: CreateTopicDto) {
    await this.findMeetingById(meetingId);

    // Get the max sort order for the category
    const maxSortOrder = await this.prisma.morningMeetingTopic.findFirst({
      where: { meetingId, category: createDto.category },
      orderBy: { sortOrder: 'desc' },
      select: { sortOrder: true },
    });

    return this.prisma.morningMeetingTopic.create({
      data: {
        meetingId,
        category: createDto.category,
        title: createDto.title,
        value: createDto.value,
        target: createDto.target,
        unit: createDto.unit,
        status: createDto.status || 'NEUTRAL',
        trend: createDto.trend,
        sortOrder: createDto.sortOrder ?? (maxSortOrder?.sortOrder ?? 0) + 1,
        note: createDto.note,
      },
    });
  }

  /**
   * Update a topic
   */
  async updateTopic(id: string, updateDto: UpdateTopicDto) {
    const topic = await this.prisma.morningMeetingTopic.findUnique({
      where: { id },
    });

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

    return this.prisma.morningMeetingTopic.update({
      where: { id },
      data: updateDto,
    });
  }

  /**
   * Delete a topic
   */
  async deleteTopic(id: string) {
    const topic = await this.prisma.morningMeetingTopic.findUnique({
      where: { id },
    });

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

    return this.prisma.morningMeetingTopic.delete({
      where: { id },
    });
  }

  // ============================================================================
  // ACTION OPERATIONS
  // ============================================================================

  /**
   * Add an action item to a meeting
   */
  async addAction(meetingId: string, createDto: CreateActionDto) {
    await this.findMeetingById(meetingId);

    // Validate assignee if provided
    if (createDto.assigneeId) {
      const assignee = await this.prisma.user.findUnique({
        where: { id: createDto.assigneeId },
      });
      if (!assignee) {
        throw new NotFoundException(
          `Assignee with ID ${createDto.assigneeId} not found`,
        );
      }
    }

    return this.prisma.morningMeetingAction.create({
      data: {
        meetingId,
        title: createDto.title,
        description: createDto.description,
        assigneeId: createDto.assigneeId,
        dueDate: createDto.dueDate ? new Date(createDto.dueDate) : null,
        status: createDto.status || 'OPEN',
        priority: createDto.priority || 'MEDIUM',
      },
      include: {
        assignee: {
          select: {
            id: true,
            firstName: true,
            lastName: true,
            email: true,
            avatar: true,
          },
        },
      },
    });
  }

  /**
   * Update an action item
   */
  async updateAction(id: string, updateDto: UpdateActionDto) {
    const action = await this.prisma.morningMeetingAction.findUnique({
      where: { id },
    });

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

    // Validate assignee if changing
    if (updateDto.assigneeId) {
      const assignee = await this.prisma.user.findUnique({
        where: { id: updateDto.assigneeId },
      });
      if (!assignee) {
        throw new NotFoundException(
          `Assignee with ID ${updateDto.assigneeId} not found`,
        );
      }
    }

    return this.prisma.morningMeetingAction.update({
      where: { id },
      data: {
        ...(updateDto.title !== undefined && { title: updateDto.title }),
        ...(updateDto.description !== undefined && {
          description: updateDto.description,
        }),
        ...(updateDto.assigneeId !== undefined && {
          assigneeId: updateDto.assigneeId,
        }),
        ...(updateDto.dueDate !== undefined && {
          dueDate: updateDto.dueDate ? new Date(updateDto.dueDate) : null,
        }),
        ...(updateDto.status !== undefined && { status: updateDto.status }),
        ...(updateDto.priority !== undefined && { priority: updateDto.priority }),
        ...(updateDto.completedAt !== undefined && {
          completedAt: updateDto.completedAt
            ? new Date(updateDto.completedAt)
            : null,
        }),
      },
      include: {
        assignee: {
          select: {
            id: true,
            firstName: true,
            lastName: true,
            email: true,
            avatar: true,
          },
        },
      },
    });
  }

  /**
   * Complete an action item
   */
  async completeAction(id: string) {
    const action = await this.prisma.morningMeetingAction.findUnique({
      where: { id },
    });

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

    if (action.status === 'COMPLETED') {
      throw new BadRequestException('Action is already completed');
    }

    return this.prisma.morningMeetingAction.update({
      where: { id },
      data: {
        status: 'COMPLETED',
        completedAt: new Date(),
      },
      include: {
        assignee: {
          select: {
            id: true,
            firstName: true,
            lastName: true,
            email: true,
            avatar: true,
          },
        },
      },
    });
  }

  /**
   * Get all open actions (across all meetings)
   */
  async getOpenActions(departmentId?: string) {
    return this.prisma.morningMeetingAction.findMany({
      where: {
        status: { in: ['OPEN', 'IN_PROGRESS'] },
        ...(departmentId && {
          meeting: { departmentId },
        }),
      },
      orderBy: [{ priority: 'desc' }, { dueDate: 'asc' }],
      include: {
        assignee: {
          select: {
            id: true,
            firstName: true,
            lastName: true,
            email: true,
            avatar: true,
          },
        },
        meeting: {
          select: {
            id: true,
            date: true,
            department: {
              select: {
                id: true,
                name: true,
              },
            },
          },
        },
      },
    });
  }

  // ============================================================================
  // KPI TREND ANALYSIS
  // ============================================================================

  /**
   * Get KPI trends over time for a department
   */
  async getKpiTrends(departmentId: string, category?: SQCDMCategory, days = 30) {
    const startDate = new Date();
    startDate.setDate(startDate.getDate() - days);

    const meetings = await this.prisma.morningMeeting.findMany({
      where: {
        departmentId,
        date: { gte: startDate },
      },
      orderBy: { date: 'asc' },
      include: {
        topics: {
          where: category ? { category } : {},
          orderBy: { sortOrder: 'asc' },
        },
      },
    });

    // Transform data for trend visualization
    const trends: Record<
      string,
      Array<{
        date: string;
        value: string | null;
        status: string;
        trend: string | null;
      }>
    > = {};

    for (const meeting of meetings) {
      for (const topic of meeting.topics) {
        const key = `${topic.category}-${topic.title}`;
        if (!trends[key]) {
          trends[key] = [];
        }
        trends[key].push({
          date: meeting.date.toISOString().split('T')[0],
          value: topic.value,
          status: topic.status,
          trend: topic.trend,
        });
      }
    }

    return {
      departmentId,
      period: { from: startDate, to: new Date() },
      trends,
    };
  }

  // ============================================================================
  // HELPER METHODS
  // ============================================================================

  /**
   * Get standard includes for meeting queries
   */
  private getMeetingIncludes() {
    return {
      department: {
        select: {
          id: true,
          name: true,
          code: true,
        },
      },
      conductor: {
        select: {
          id: true,
          firstName: true,
          lastName: true,
          email: true,
          avatar: true,
        },
      },
      topics: {
        orderBy: [
          { category: 'asc' as const },
          { sortOrder: 'asc' as const },
        ],
      },
      actions: {
        orderBy: [
          { priority: 'desc' as const },
          { createdAt: 'desc' as const },
        ],
        include: {
          assignee: {
            select: {
              id: true,
              firstName: true,
              lastName: true,
              email: true,
              avatar: true,
            },
          },
        },
      },
    };
  }
}
