import {
    Injectable,
    NotFoundException,
    ConflictException,
} from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreatePricingHeaderDto, UpdatePricingHeaderDto } from './dto/pricing-header.dto';
import { Prisma } from 'generated/prisma/client';
import { BootstrapTableRequestDto } from 'src/commons/dto/bootstrap-table-request.dto';
import { BootstrapTableResponseDto } from 'src/commons/dto/bootstrap-table-response.dto';
import { buildWhereClause } from 'src/commons/utils/prisma-query-builder';

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

    async findAll(query: BootstrapTableRequestDto, itemTypeId?: string) {
        const allowedFilters = {
            code: 'string',
            name: 'string',
            status: 'enum',
            unit: 'string',
            effectiveDate: 'dateRange',
        } as const

        const where = buildWhereClause(
            query,
            allowedFilters,
            ['code', 'name'],
        )

        // Inject itemTypeId filter if provided via query param
        if (itemTypeId) {
            where.itemTypeId = itemTypeId
        }

        const allowedSortColumns = ['code', 'name', 'weight', 'unit', 'status', 'effectiveDate', 'createdAt']

        const orderBy: Prisma.PricingHeaderOrderByWithRelationInput = {}
        if (query.sort && allowedSortColumns.includes(query.sort)) {
            orderBy[query.sort] = query.sortOrder
        } else {
            orderBy.createdAt = 'desc'
        }

        const [total, headers] = await this.prisma.$transaction([
            this.prisma.pricingHeader.count({ where }),
            this.prisma.pricingHeader.findMany({
                where,
                skip: query.skip,
                take: query.take,
                orderBy,
                include: { itemType: true },
            }),
        ])

        const now = new Date()
        const rows = await Promise.all(
            headers.map(async (header) => {
                const activePrice = await this.prisma.pricingDetail.findFirst({
                    where: {
                        headerId: header.id,
                        status: 'ACTIVE',
                        beginEffective: { lte: now },
                        OR: [{ endEffective: null }, { endEffective: { gte: now } }],
                    },
                    orderBy: { beginEffective: 'desc' },
                    select: {
                        id: true,
                        pricePerUnit: true,
                        beginEffective: true,
                    },
                })
                return { ...header, activePrice }
            })
        )

        return new BootstrapTableResponseDto(total, rows)
    }

    async findOne(id: string) {
        const header = await this.prisma.pricingHeader.findUnique({
            where: { id },
            include: {
                itemType: true,
                pricingDetails: {
                    orderBy: { beginEffective: 'desc' },
                },
            },
        })
        if (!header)
            throw new NotFoundException(`PricingHeader with id ${id} not found`)
        return header
    }

    async create(dto: CreatePricingHeaderDto) {
        const existing = await this.prisma.pricingHeader.findUnique({
            where: { code: dto.code },
        })
        if (existing)
            throw new ConflictException(`Code "${dto.code}" already exists`)

        return this.prisma.pricingHeader.create({
            data: {
                ...dto,
                effectiveDate: new Date(dto.effectiveDate),
            },
            include: { itemType: true },
        })
    }

    async update(id: string, dto: UpdatePricingHeaderDto) {
        await this.findOne(id)

        if (dto.code) {
            const conflict = await this.prisma.pricingHeader.findFirst({
                where: { code: dto.code, NOT: { id } },
            })
            if (conflict)
                throw new ConflictException(`Code "${dto.code}" already exists`)
        }

        return this.prisma.pricingHeader.update({
            where: { id },
            data: {
                ...dto,
                ...(dto.effectiveDate ? { effectiveDate: new Date(dto.effectiveDate) } : {}),
            },
            include: { itemType: true },
        })
    }

    async remove(id: string) {
        await this.findOne(id)
        return this.prisma.pricingHeader.delete({ where: { id } })
    }
}