import { ConflictException, Injectable, NotFoundException } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../prisma/prisma.service';
import { CreateJobDto, QueryJobsDto, UpdateJobDto } from './dto/jobs.dto';

const JOB_SELECT = {
  id: true,
  ticketNumber: true,
  title: true,
  description: true,
  address: true,
  status: true,
  source: true,
  dueAt: true,
  startedAt: true,
  completedAt: true,
  createdAt: true,
  updatedAt: true,
  assignedTo: { select: { id: true, name: true, email: true } },
  createdBy: { select: { id: true, name: true, email: true } },
  createdByDeviceId: true,
  _count: { select: { points: true } },
} satisfies Prisma.JobSelect;

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

  async list(orgId: string, query: QueryJobsDto) {
    const where: Prisma.JobWhereInput = {
      orgId,
      ...(query.status && { status: query.status }),
      ...(query.assignedToId && { assignedToId: query.assignedToId }),
      ...(query.q && {
        OR: [
          { ticketNumber: { contains: query.q, mode: 'insensitive' } },
          { title: { contains: query.q, mode: 'insensitive' } },
          { address: { contains: query.q, mode: 'insensitive' } },
        ],
      }),
    };
    const [jobs, total] = await this.prisma.$transaction([
      this.prisma.job.findMany({
        where,
        select: JOB_SELECT,
        orderBy: { createdAt: 'desc' },
        take: query.limit ?? 50,
        skip: query.offset ?? 0,
      }),
      this.prisma.job.count({ where }),
    ]);
    return { jobs, total };
  }

  async get(orgId: string, jobId: string) {
    const job = await this.prisma.job.findFirst({
      where: { id: jobId, orgId },
      select: JOB_SELECT,
    });
    if (!job) {
      throw new NotFoundException('Job not found');
    }
    return job;
  }

  async create(orgId: string, dto: CreateJobDto, createdById: string | null) {
    const existing = await this.prisma.job.findUnique({
      where: { orgId_ticketNumber: { orgId, ticketNumber: dto.ticketNumber } },
    });
    if (existing) {
      throw new ConflictException(`Ticket ${dto.ticketNumber} already exists in this organization`);
    }
    return this.prisma.job.create({
      data: {
        orgId,
        ticketNumber: dto.ticketNumber,
        title: dto.title,
        description: dto.description,
        address: dto.address,
        status: dto.status,
        assignedToId: dto.assignedToId,
        dueAt: dto.dueAt ? new Date(dto.dueAt) : undefined,
        createdById,
      },
      select: JOB_SELECT,
    });
  }

  async update(orgId: string, jobId: string, dto: UpdateJobDto) {
    await this.get(orgId, jobId);
    const statusTimestamps: Prisma.JobUncheckedUpdateInput =
      dto.status === 'IN_PROGRESS'
        ? { startedAt: new Date() }
        : dto.status === 'COMPLETED'
          ? { completedAt: new Date() }
          : {};
    return this.prisma.job.update({
      where: { id: jobId },
      data: {
        ...(dto.title !== undefined && { title: dto.title }),
        ...(dto.description !== undefined && { description: dto.description }),
        ...(dto.address !== undefined && { address: dto.address }),
        ...(dto.status !== undefined && { status: dto.status }),
        ...(dto.assignedToId !== undefined && { assignedToId: dto.assignedToId }),
        ...(dto.dueAt !== undefined && { dueAt: dto.dueAt ? new Date(dto.dueAt) : null }),
        ...statusTimestamps,
      },
      select: JOB_SELECT,
    });
  }

  async remove(orgId: string, jobId: string) {
    await this.get(orgId, jobId);
    await this.prisma.job.delete({ where: { id: jobId } });
    return { ok: true };
  }
}
