package kr.itn.itnhub.project;

import kr.itn.itnhub.dashboard.DashboardOrgRow;
import kr.itn.itnhub.dashboard.DashboardService;
import kr.itn.itnhub.org.OrgNotFoundException;
import kr.itn.itnhub.org.OrganizationMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 사업관리 화면. 3단계 법률검토로 진행할 기관 현황을 한 곳에 모아 보여주고, 기관마다
 * "신청 확인 연락" 이력과 최종 보고서를 관리한다(요청자 요구: 별도 메뉴로 구축 + 연락 부분 이력관리
 * + 기관별 최종 보고서 업로드).
 *
 * <p>기관 현황 숫자는 대시보드와 같은 집계를 그대로 쓴다 - 두 화면이 다른 숫자를 보여주면
 * 어느 쪽이 맞는지 확인할 방법이 없기 때문이다.</p>
 */
@Service
public class ProjectService {

    private final DashboardService dashboardService;
    private final ProjectMapper mapper;
    private final OrganizationMapper orgMapper;

    public ProjectService(DashboardService dashboardService, ProjectMapper mapper,
                          OrganizationMapper orgMapper) {
        this.dashboardService = dashboardService;
        this.mapper = mapper;
        this.orgMapper = orgMapper;
    }

    public List<ProjectRow> rows() {
        Map<Long, ProjectMapper.ProjectSideInfo> side = new HashMap<>();
        for (ProjectMapper.ProjectSideInfo info : mapper.findSideInfo()) {
            side.put(info.orgId(), info);
        }

        List<DashboardOrgRow> orgs = dashboardService.dashboard().orgs();
        List<ProjectRow> rows = new ArrayList<>(orgs.size());
        for (DashboardOrgRow org : orgs) {
            ProjectMapper.ProjectSideInfo info = side.get(org.id());
            rows.add(new ProjectRow(org,
                    info == null ? null : info.lastContactedOn(),
                    info == null ? 0 : info.contactCount(),
                    info == null ? 0 : info.reportCount()));
        }
        return rows;
    }

    public List<ContactLog> logs(Long orgId) {
        requireOrg(orgId);
        return mapper.findLogsByOrg(orgId);
    }

    @Transactional
    public List<ContactLog> addLog(Long orgId, ContactLogRequest request, String author) {
        requireOrg(orgId);
        mapper.insertLog(orgId, request.contactedOn(), request.method(), request.summary(), author);
        return mapper.findLogsByOrg(orgId);
    }

    @Transactional
    public List<ContactLog> updateLog(Long orgId, Long logId, ContactLogRequest request) {
        requireOrg(orgId);
        if (mapper.updateLog(orgId, logId, request.contactedOn(), request.method(),
                request.summary()) == 0) {
            throw new OrgNotFoundException("연락 이력을 찾을 수 없습니다: " + logId);
        }
        return mapper.findLogsByOrg(orgId);
    }

    @Transactional
    public void deleteLog(Long orgId, Long logId) {
        requireOrg(orgId);
        if (mapper.deleteLog(orgId, logId) == 0) {
            throw new OrgNotFoundException("연락 이력을 찾을 수 없습니다: " + logId);
        }
    }

    public List<OrgReport> reports(Long orgId) {
        requireOrg(orgId);
        return mapper.findReportsByOrg(orgId);
    }

    /** 같은 기관에 여러 번 올릴 수 있다(개정판). 최신순으로 목록에 쌓인다. */
    @Transactional
    public List<OrgReport> addReport(Long orgId, String fileName, String contentType,
                                     byte[] content, String uploadedBy) {
        requireOrg(orgId);
        if (content == null || content.length == 0) {
            throw new IllegalArgumentException("빈 파일은 올릴 수 없습니다.");
        }
        mapper.insertReport(orgId, fileName, contentType, content.length, content, uploadedBy);
        return mapper.findReportsByOrg(orgId);
    }

    /**
     * 올려둔 보고서를 새 파일로 바꾼다(요구사항 [25]). 목록에 새 줄이 생기지 않고 그 자리만
     * 바뀐다 - 개정판으로 남기고 싶으면 새로 올리면 된다.
     */
    @Transactional
    public List<OrgReport> replaceReport(Long orgId, Long reportId, String fileName,
                                         String contentType, byte[] content, String uploadedBy) {
        requireOrg(orgId);
        if (content == null || content.length == 0) {
            throw new IllegalArgumentException("빈 파일은 올릴 수 없습니다.");
        }
        int updated = mapper.updateReport(orgId, reportId, fileName, contentType, content.length,
                content, uploadedBy);
        if (updated == 0) {
            throw new OrgNotFoundException("보고서를 찾을 수 없습니다: " + reportId);
        }
        return mapper.findReportsByOrg(orgId);
    }

    public ProjectMapper.ReportContent reportContent(Long orgId, Long reportId) {
        requireOrg(orgId);
        ProjectMapper.ReportContent content = mapper.findReportContent(orgId, reportId);
        if (content == null) {
            throw new OrgNotFoundException("보고서를 찾을 수 없습니다: " + reportId);
        }
        return content;
    }

    @Transactional
    public void deleteReport(Long orgId, Long reportId) {
        requireOrg(orgId);
        if (mapper.deleteReport(orgId, reportId) == 0) {
            throw new OrgNotFoundException("보고서를 찾을 수 없습니다: " + reportId);
        }
    }

    private void requireOrg(Long orgId) {
        if (orgMapper.findById(orgId) == null) {
            throw new OrgNotFoundException("기관을 찾을 수 없습니다: " + orgId);
        }
    }
}
