import {
    Injectable,
    NotFoundException,
} from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import {
    CreatePricingDetailDto,
    UpdatePricingDetailDto,
    UpdatePriceDto,
} from './dto/pricing-detail.dto';
import { Prisma, SettingStatus } 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 PricingDetailService {
    constructor(private readonly prisma: PrismaService) { }

    async findAll(query: BootstrapTableRequestDto, headerId?: string, itemTypeId?: string) {
        const allowedFilters = {
            unit: 'string',
            status: 'enum',
            beginEffective: 'dateRange',
            endEffective: 'dateRange',
        } as const

        const where = buildWhereClause(
            query,
            allowedFilters,
            [], // no meaningful global text search on pricing detail
        )

        if (headerId) where.headerId = headerId
        if (itemTypeId) where.itemTypeId = itemTypeId

        const allowedSortColumns = [
            'pricePerUnit',
            'weight',
            'unit',
            'status',
            'beginEffective',
            'endEffective',
            'createdAt',
        ]

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

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

        return new BootstrapTableResponseDto(total, rows)
    }

    async findOne(id: string) {
        const detail = await this.prisma.pricingDetail.findUnique({
            where: { id },
            include: { header: true, itemType: true },
        })
        if (!detail)
            throw new NotFoundException(`PricingDetail with id ${id} not found`)
        return detail
    }

    /**
     * Get the currently active price for a given header
     */
    async findActiveByHeader(headerId: string) {
        const now = new Date()
        const detail = await this.prisma.pricingDetail.findFirst({
            where: {
                headerId,
                status: SettingStatus.ACTIVE,
                beginEffective: { lte: now },
                OR: [{ endEffective: null }, { endEffective: { gte: now } }],
            },
            orderBy: { beginEffective: 'desc' },
            include: { header: true, itemType: true },
        })
        if (!detail)
            throw new NotFoundException(`No active pricing found for header ${headerId}`)
        return detail
    }

    async create(dto: CreatePricingDetailDto) {
        return this.prisma.pricingDetail.create({
            data: {
                ...dto,
                beginEffective: new Date(dto.beginEffective),
                endEffective: dto.endEffective ? new Date(dto.endEffective) : null,
            },
            include: { header: true, itemType: true },
        })
    }

    async update(id: string, dto: UpdatePricingDetailDto) {
        await this.findOne(id)
        return this.prisma.pricingDetail.update({
            where: { id },
            data: {
                ...dto,
                ...(dto.beginEffective ? { beginEffective: new Date(dto.beginEffective) } : {}),
                ...(dto.endEffective ? { endEffective: new Date(dto.endEffective) } : {}),
            },
            include: { header: true, itemType: true },
        })
    }

    /**
     * Update price for a header.
     * Closes the previous active record (sets endEffective) and creates a new one.
     * This preserves full price history.
     */
    async updatePrice(headerId: string, dto: UpdatePriceDto) {
        const now = new Date()

        const currentActive = await this.prisma.pricingDetail.findFirst({
            where: {
                headerId,
                status: SettingStatus.ACTIVE,
                OR: [{ endEffective: null }, { endEffective: { gte: now } }],
            },
            orderBy: { beginEffective: 'desc' },
        })

        if (!currentActive)
            throw new NotFoundException(`No active pricing found for header ${headerId}`)

        const beginEffectiveDate = new Date(dto.beginEffective)

        // Close old price record
        await this.prisma.pricingDetail.update({
            where: { id: currentActive.id },
            data: { endEffective: beginEffectiveDate, status: SettingStatus.INACTIVE },
        })

        // Create new price record
        return this.prisma.pricingDetail.create({
            data: {
                headerId,
                itemTypeId: currentActive.itemTypeId,
                pricePerUnit: dto.pricePerUnit,
                weight: currentActive.weight,
                unit: currentActive.unit,
                status: SettingStatus.ACTIVE,
                beginEffective: beginEffectiveDate,
                endEffective: null,
                createdBy: dto.modifiedBy,
                modifiedBy: dto.modifiedBy,
            },
            include: { header: true, itemType: true },
        })
    }

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