package kr.co.accura.wtm.domain.report.service;

import kr.co.accura.wtm.domain.report.dto.ProjectHoursReport;
import kr.co.accura.wtm.domain.report.dto.ReportFilter;
import kr.co.accura.wtm.domain.report.dto.WbsHoursReport;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class ReportService {

    private final EntityManager em;

    public ProjectHoursReport getProjectHoursReport(ReportFilter filter) {
        StringBuilder sql = new StringBuilder(
            "SELECT te.epc_project_id, p.project_code, p.name, '' as discipline, " +
            "te.entry_date, SUM(te.hours) " +
            "FROM timesheet_entries te " +
            "JOIN timesheets ts ON te.timesheet_id = ts.id " +
            "LEFT JOIN projects p ON te.epc_project_id = p.id " +
            "WHERE ts.status = 'APPROVED' ");

        if (filter.projectId() != null) {
            sql.append("AND te.epc_project_id = :projectId ");
        }
        if (filter.from() != null) {
            sql.append("AND te.entry_date >= :fromDate ");
        }
        if (filter.to() != null) {
            sql.append("AND te.entry_date <= :toDate ");
        }
        sql.append("GROUP BY te.epc_project_id, p.project_code, p.name, te.entry_date ");
        sql.append("ORDER BY te.entry_date");

        Query query = em.createNativeQuery(sql.toString());
        if (filter.projectId() != null) query.setParameter("projectId", filter.projectId());
        if (filter.from() != null) query.setParameter("fromDate", filter.from());
        if (filter.to() != null) query.setParameter("toDate", filter.to());

        @SuppressWarnings("unchecked")
        List<Object[]> results = query.getResultList();

        List<ProjectHoursReport.ProjectHoursRow> rows = new ArrayList<>();
        BigDecimal totalHours = BigDecimal.ZERO;

        for (Object[] row : results) {
            BigDecimal hours = row[5] != null ? new BigDecimal(row[5].toString()) : BigDecimal.ZERO;
            rows.add(new ProjectHoursReport.ProjectHoursRow(
                    row[0] != null ? ((Number) row[0]).longValue() : null,
                    row[1] != null ? row[1].toString() : null,
                    row[2] != null ? row[2].toString() : null,
                    row[3] != null ? row[3].toString() : null,
                    row[4] != null ? java.time.LocalDate.parse(row[4].toString()) : null,
                    hours
            ));
            totalHours = totalHours.add(hours);
        }

        return new ProjectHoursReport(filter, rows, totalHours, LocalDateTime.now());
    }

    public WbsHoursReport getWbsHoursReport(ReportFilter filter) {
        StringBuilder sql = new StringBuilder(
            "SELECT cw.wbs_code, cw.name, cw.level, cw.discipline, " +
            "SUM(te.hours), COUNT(DISTINCT ts.user_id) " +
            "FROM timesheet_entries te " +
            "JOIN timesheets ts ON te.timesheet_id = ts.id " +
            "JOIN canonical_wbs cw ON te.canonical_wbs_id = cw.id " +
            "WHERE ts.status = 'APPROVED' ");

        if (filter.projectId() != null) {
            sql.append("AND cw.project_id = :projectId ");
        }
        if (filter.wbsLevel() != null) {
            sql.append("AND cw.level = :wbsLevel ");
        }
        if (filter.from() != null) {
            sql.append("AND te.entry_date >= :fromDate ");
        }
        if (filter.to() != null) {
            sql.append("AND te.entry_date <= :toDate ");
        }
        sql.append("GROUP BY cw.wbs_code, cw.name, cw.level, cw.discipline ");
        sql.append("ORDER BY cw.wbs_code");

        Query query = em.createNativeQuery(sql.toString());
        if (filter.projectId() != null) query.setParameter("projectId", filter.projectId());
        if (filter.wbsLevel() != null) query.setParameter("wbsLevel", filter.wbsLevel());
        if (filter.from() != null) query.setParameter("fromDate", filter.from());
        if (filter.to() != null) query.setParameter("toDate", filter.to());

        @SuppressWarnings("unchecked")
        List<Object[]> results = query.getResultList();

        List<WbsHoursReport.WbsHoursRow> rows = new ArrayList<>();
        for (Object[] row : results) {
            rows.add(new WbsHoursReport.WbsHoursRow(
                    row[0] != null ? row[0].toString() : null,
                    row[1] != null ? row[1].toString() : null,
                    row[2] != null ? ((Number) row[2]).intValue() : 0,
                    row[3] != null ? row[3].toString() : null,
                    row[4] != null ? new BigDecimal(row[4].toString()) : BigDecimal.ZERO,
                    row[5] != null ? ((Number) row[5]).longValue() : 0
            ));
        }

        return new WbsHoursReport(filter, rows, LocalDateTime.now());
    }
}
