package com.ffii.lioner.modules.lioner.pdf.service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.ffii.lioner.modules.lioner.pdf.entity.FormSigPage;
import com.ffii.lioner.modules.lioner.pdf.entity.FormSigPageRepository;
import com.ffii.lioner.modules.lioner.pdf.req.UpdateFormSigPageReq;

@Service
public class FormSigPageService {

    private static final String UPLOAD1 = "upload1";
    private static final String UPLOAD2 = "upload2";
    private static final String UPLOAD3 = "upload3";

    private final FormSigPageRepository formSigPageRepository;

    public FormSigPageService(FormSigPageRepository formSigPageRepository) {
        this.formSigPageRepository = formSigPageRepository;
    }

    /** List all non-deleted records, optionally filtered by formCode and sigType. */
    public List<FormSigPage> list(String formCode, String sigType) {
        List<FormSigPage> all = formSigPageRepository.findByDeletedFalseOrderByFormCodeAscStartDateDesc();
        if (formCode != null && !formCode.isBlank()) {
            all = all.stream().filter(f -> formCode.equals(f.getFormCode())).collect(Collectors.toList());
        }
        if (sigType != null && !sigType.isBlank()) {
            all = all.stream().filter(f -> sigType.equals(f.getSigType())).collect(Collectors.toList());
        }
        return all;
    }

    public Optional<FormSigPage> find(Long id) {
        return formSigPageRepository.findById(id).filter(f -> !Boolean.TRUE.equals(f.getDeleted()));
    }

    @Transactional(rollbackFor = Exception.class)
    public FormSigPage save(UpdateFormSigPageReq req) {
        FormSigPage entity;
        // Only update when id is present and positive (existing record); otherwise create (id null or <= 0)
        if (req.getId() != null && req.getId() > 0) {
            entity = formSigPageRepository.findById(req.getId()).orElseThrow();
            entity.setFormCode(req.getFormCode());
            entity.setStartDate(req.getStartDate());
            entity.setSigType(req.getSigType());
            entity.setPageFrom(req.getPageFrom());
            entity.setPageTo(req.getPageTo());
        } else {
            entity = new FormSigPage();
            entity.setFormCode(req.getFormCode());
            entity.setStartDate(req.getStartDate());
            entity.setSigType(req.getSigType());
            entity.setPageFrom(req.getPageFrom());
            entity.setPageTo(req.getPageTo());
        }
        return formSigPageRepository.save(entity);
    }

    @Transactional(rollbackFor = Exception.class)
    public void markDelete(Long id) {
        formSigPageRepository.findById(id).ifPresent(entity -> {
            entity.setDeleted(Boolean.TRUE);
            formSigPageRepository.save(entity);
        });
    }

    /**
     * Resolves the form_sig_page record valid for the given formCode and as-of date for a sig type.
     * Uses the latest startDate <= asOfDate.
     */
    public Optional<FormSigPage> getConfig(String formCode, LocalDate asOfDate, String sigType) {
        if (formCode == null || asOfDate == null || sigType == null) {
            return Optional.empty();
        }
        List<FormSigPage> list = formSigPageRepository
                .findByFormCodeAndSigTypeAndStartDateLessThanEqualAndDeletedFalseOrderByStartDateDesc(
                        formCode, sigType, asOfDate);
        return list.isEmpty() ? Optional.empty() : Optional.of(list.get(0));
    }

    public Optional<FormSigPage> getUpload1Config(String formCode, LocalDate asOfDate) {
        return getConfig(formCode, asOfDate, UPLOAD1);
    }

    public Optional<FormSigPage> getUpload2Config(String formCode, LocalDate asOfDate) {
        return getConfig(formCode, asOfDate, UPLOAD2);
    }

    public Optional<FormSigPage> getUpload3Config(String formCode, LocalDate asOfDate) {
        return getConfig(formCode, asOfDate, UPLOAD3);
    }

    /**
     * Converts LocalDateTime (e.g. filled_form.created) to LocalDate for lookup.
     */
    public Optional<FormSigPage> getUpload1Config(String formCode, LocalDateTime asOf) {
        return asOf == null ? Optional.empty() : getUpload1Config(formCode, asOf.toLocalDate());
    }

    public Optional<FormSigPage> getUpload2Config(String formCode, LocalDateTime asOf) {
        return asOf == null ? Optional.empty() : getUpload2Config(formCode, asOf.toLocalDate());
    }

    public Optional<FormSigPage> getUpload3Config(String formCode, LocalDateTime asOf) {
        return asOf == null ? Optional.empty() : getUpload3Config(formCode, asOf.toLocalDate());
    }

    /**
     * Returns config for upload1, upload2, and upload3 for the given formCode and asOf date.
     * Used by merge service and by the API for frontend labels.
     */
    public Map<String, FormSigPage> getConfigForForm(String formCode, LocalDate asOfDate) {
        Map<String, FormSigPage> map = new HashMap<>();
        getUpload1Config(formCode, asOfDate).ifPresent(c -> map.put(UPLOAD1, c));
        getUpload2Config(formCode, asOfDate).ifPresent(c -> map.put(UPLOAD2, c));
        getUpload3Config(formCode, asOfDate).ifPresent(c -> map.put(UPLOAD3, c));
        return map;
    }

    public Map<String, FormSigPage> getConfigForForm(String formCode, LocalDateTime asOf) {
        return asOf == null ? Map.of() : getConfigForForm(formCode, asOf.toLocalDate());
    }

    /**
     * Returns DTOs for API: list of { sigType, pageFrom, pageTo, label } (upload1, upload2, upload3 when present).
     */
    public List<Map<String, Object>> getConfigDtosForForm(String formCode, LocalDate asOfDate) {
        List<Map<String, Object>> out = new ArrayList<>();
        getUpload1Config(formCode, asOfDate).map(this::toDto).ifPresent(out::add);
        getUpload2Config(formCode, asOfDate).map(this::toDto).ifPresent(out::add);
        getUpload3Config(formCode, asOfDate).map(this::toDto).ifPresent(out::add);
        return out;
    }

    public List<Map<String, Object>> getConfigDtosForForm(String formCode, LocalDateTime asOf) {
        return asOf == null ? List.of() : getConfigDtosForForm(formCode, asOf.toLocalDate());
    }

    private Map<String, Object> toDto(FormSigPage c) {
        Map<String, Object> m = new HashMap<>();
        m.put("sigType", c.getSigType());
        m.put("pageFrom", c.getPageFrom());
        m.put("pageTo", c.getPageTo());
        m.put("label", c.getPageFrom().equals(c.getPageTo())
                ? "Upload Page " + c.getPageFrom()
                : "Upload Page " + c.getPageFrom() + "-" + c.getPageTo());
        return m;
    }
}
