package kr.itn.itnhub.contact;

import kr.itn.itnhub.org.Organization;
import kr.itn.itnhub.org.OrganizationMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;

import java.io.InputStream;

/**
 * 회원정보 xlsx를 읽어 담당자를 등록/갱신하고, 신청기관 담당자는 같은 이름의 기관에
 * 자동 배정한다. 재업로드해도 안전해야 한다(SeedService와 같은 원칙) - 같은 사람이
 * 다시 올라오면 새로 만들지 않고 기존 담당자를 찾아 재사용한다.
 *
 * <p>엑셀 파싱은 DB와 무관하므로 트랜잭션 밖에서 수행한다(kr.itn.itnhub.seed.SeedService와
 * 동일한 이유 - 같은 빈의 @Transactional 메서드를 self-invocation으로 호출하면 프록시를
 * 우회해 트랜잭션이 적용되지 않으므로 TransactionTemplate으로 감싼다).</p>
 */
@Service
public class MemberImportService {

    private final MemberDirectoryParser parser;
    private final ContactMapper contactMapper;
    private final OrganizationMapper orgMapper;
    private final TransactionTemplate transactionTemplate;

    public MemberImportService(MemberDirectoryParser parser, ContactMapper contactMapper,
                                OrganizationMapper orgMapper, PlatformTransactionManager transactionManager) {
        this.parser = parser;
        this.contactMapper = contactMapper;
        this.orgMapper = orgMapper;
        this.transactionTemplate = new TransactionTemplate(transactionManager);
    }

    public MemberImportReport importFile(InputStream xlsx) {
        MemberParseResult parsed = parser.parse(xlsx);
        return transactionTemplate.execute(status -> importRows(parsed));
    }

    private MemberImportReport importRows(MemberParseResult parsed) {
        int created = 0;
        int updated = 0;
        int assigned = 0;

        for (MemberRow row : parsed.rows()) {
            Contact existing = contactMapper.findMatching(
                    row.category(), row.name(), row.phone(), row.email());

            Contact contact;
            if (existing == null) {
                contact = new Contact();
                contact.setCategory(row.category());
                contact.setName(row.name());
                contact.setAffiliation(row.affiliation());
                contact.setDeptName(row.deptName());
                contact.setTitle(row.title());
                contact.setPhone(row.phone());
                contact.setEmail(row.email());
                contactMapper.insert(contact);
                created++;
            } else {
                fillMissingFields(existing, row);
                contactMapper.update(existing);
                updated++;
                contact = existing;
            }

            if ("APPLICANT".equals(row.category()) && tryAssignApplicant(row, contact)) {
                assigned++;
            }
        }

        return new MemberImportReport(created, updated, parsed.skipped(), assigned);
    }

    /** 기존 담당자의 소속/부서/직급은 이미 값이 있으면 건드리지 않고, 비어 있을 때만 채운다. */
    private void fillMissingFields(Contact existing, MemberRow row) {
        if (existing.getAffiliation() == null && row.affiliation() != null) {
            existing.setAffiliation(row.affiliation());
        }
        if (existing.getDeptName() == null && row.deptName() != null) {
            existing.setDeptName(row.deptName());
        }
        if (existing.getTitle() == null && row.title() != null) {
            existing.setTitle(row.title());
        }
    }

    /** 기관명이 정확히 일치하고 아직 신청기관 담당자가 배정되지 않은 기관에만 배정한다. */
    private boolean tryAssignApplicant(MemberRow row, Contact contact) {
        if (row.affiliation() == null) {
            return false;
        }
        Organization org = orgMapper.findByOrgName(row.affiliation());
        if (org == null || org.getApplicantContactId() != null) {
            return false;
        }
        orgMapper.updateAssignments(org.getId(), contact.getId(),
                org.getMjContactId(), org.getItnContactId(),
                org.getLawyerContactId(), org.getLawyerAssignedDate());
        return true;
    }
}
