import { Injectable, NotFoundException } from '@nestjs/common'
import { Prisma } from 'generated/prisma/client'
import { buildWhereClause } from 'src/commons/utils/prisma-query-builder'
import { ExportQueryDto } from 'src/export/export.types'
import { StorageService } from 'src/storage/storage.service'
import { BootstrapTableRequestDto } from '../commons/dto/bootstrap-table-request.dto'
import { BootstrapTableResponseDto } from '../commons/dto/bootstrap-table-response.dto'
import { PrismaService } from '../prisma/prisma.service'
import { CreateCustomerDto } from './dto/create-customer.dto'
import { UpdateCustomerDto } from './dto/update-customer.dto'

@Injectable()
export class CustomerService {
    constructor(private prisma: PrismaService, private storage: StorageService) { }

    async create(dto: CreateCustomerDto) {

        const prefix = dto.fullName
            .replace(/[^a-zA-Z]/g, '')
            .toUpperCase()
            .slice(0, 3)
            .padEnd(3, 'X')

        const count = await this.prisma.customer.count()
        const number = String(count + 1).padStart(3, '0')
        const code = `${prefix}${number}`

        // Promote KTP photo if provided
        let ktpPhotoUrl: string | undefined = undefined;

        if (dto.ktpPhotoTempId) {
            const temp = await this.prisma.tempPhoto.findUniqueOrThrow({
                where: { id: dto.ktpPhotoTempId },
            });

            ktpPhotoUrl = await this.storage.promoteToPermament(
                temp.url,
                `customers`,
            );

            await this.prisma.tempPhoto.delete({ where: { id: temp.id } });
        }

        // Destructure out tempId so it doesn't get passed to prisma
        const { ktpPhotoTempId, ...customerData } = dto;

        return this.prisma.customer.create({
            data: {
                ...customerData,
                code,
                ktpPhotoUrl,
            },
        });
    }

    async findAll(query: BootstrapTableRequestDto) {
        const allowedFilters = {
            code: 'string',
            fullName: 'string',
            phoneNumber: 'string',
            ktpNumber: 'string',
            address: 'string',
        } as const

        const where = buildWhereClause(
            query,
            allowedFilters,
            ['code', 'fullName', 'phoneNumber', 'ktpNumber', 'address'],
        )

        const allowedSortColumns = [
            'code',
            'fullName',
            'phoneNumber',
            'ktpNumber',
            'address',
            'createdAt',
        ]

        const orderBy: Prisma.CustomerOrderByWithRelationInput = {}

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

        const [total, rows] = await this.prisma.$transaction([
            this.prisma.customer.count({ where }),
            this.prisma.customer.findMany({
                where,
                skip: query.skip,
                take: query.take,
                orderBy,
                include: {
                    _count: {
                        select: {
                            transactions: {
                                where: {
                                    status: 'COMPLETED'
                                }
                            }
                        }
                    },
                    transactions: {
                        select: {
                            totalAmount: true
                        },
                        where: {
                            status: 'COMPLETED'
                        }
                    }
                }
            }),
        ])

        const formattedRows = rows.map(c => ({
            // TODO: obscure sensitive data like phone number and ktp number
            ...c,
            totalAmount: c.transactions.reduce(
                (sum, t) => sum + Number(t.totalAmount ?? 0),
                0
            ),
            transactionCount: c._count.transactions,
            transactions: undefined
        }))

        return new BootstrapTableResponseDto(total, formattedRows)
    }

    findOne(id: string) {
        return this.prisma.customer.findUnique({
            where: { id },
            include: {
                transactions: true,
            },
        })
    }

    async update(id: string, dto: UpdateCustomerDto) {
        const existing = await this.prisma.customer.findUnique({ where: { id } });
        if (!existing) {
            throw new NotFoundException(`Customer with id ${id} not found`);
        }

        let ktpPhotoUrl: string | undefined = undefined;

        if (dto.ktpPhotoTempId) {
            const temp = await this.prisma.tempPhoto.findUnique({
                where: { id: dto.ktpPhotoTempId },
            });

            if (!temp) {
                throw new NotFoundException(`Temp photo not found. Please re-upload the image first.`);
            }

            // ✅ Delete old permanent file if exists
            if (existing.ktpPhotoUrl) {
                await this.storage.deleteFile(existing.ktpPhotoUrl);
            }

            // ✅ Promote new temp to permanent
            ktpPhotoUrl = await this.storage.promoteToPermament(
                temp.url,
                `customers`,
            );

            await this.prisma.tempPhoto.delete({ where: { id: temp.id } });
        }

        const { ktpPhotoTempId, ...customerData } = dto;

        return this.prisma.customer.update({
            where: { id },
            data: {
                ...customerData,
                ...(ktpPhotoUrl && { ktpPhotoUrl }),
            },
        });
    }

    async getCustomersForExport(query: ExportQueryDto) {
        return this.prisma.customer.findMany({
            include: {
                _count: {
                    select: { transactions: true },
                },
            },
            orderBy: { createdAt: 'asc' },
        });
    }

    remove(id: string) {
        return this.prisma.customer.delete({
            where: { id },
        })
    }
}