package de.cannamanage.service;

import de.cannamanage.domain.constants.ComplianceConstants;
import de.cannamanage.domain.entity.Distribution;
import de.cannamanage.domain.entity.Member;
import de.cannamanage.domain.entity.Strain;
import de.cannamanage.domain.entity.User;
import de.cannamanage.service.dto.QuotaStatus;
import de.cannamanage.service.dto.portal.PortalDashboard;
import de.cannamanage.service.dto.portal.PortalDistributionHistory;
import de.cannamanage.service.dto.portal.PortalProfile;
import de.cannamanage.service.dto.portal.PortalQuota;
import de.cannamanage.service.exception.MemberNotFoundException;
import de.cannamanage.service.repository.BatchRepository;
import de.cannamanage.service.repository.DistributionRepository;
import de.cannamanage.service.repository.MemberRepository;
import de.cannamanage.service.repository.StrainRepository;
import de.cannamanage.service.repository.UserRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.util.List;
import java.util.UUID;

/**
 * Service layer for the member self-service portal.
 * All methods enforce member-scoped data access — only the specified memberId's data is returned.
 */
@Service
@Transactional(readOnly = true)
public class PortalService {

    private final MemberRepository memberRepository;
    private final DistributionRepository distributionRepository;
    private final ComplianceService complianceService;
    private final BatchRepository batchRepository;
    private final StrainRepository strainRepository;
    private final UserRepository userRepository;

    public PortalService(MemberRepository memberRepository,
                         DistributionRepository distributionRepository,
                         ComplianceService complianceService,
                         BatchRepository batchRepository,
                         StrainRepository strainRepository,
                         UserRepository userRepository) {
        this.memberRepository = memberRepository;
        this.distributionRepository = distributionRepository;
        this.complianceService = complianceService;
        this.batchRepository = batchRepository;
        this.strainRepository = strainRepository;
        this.userRepository = userRepository;
    }

    /**
     * Dashboard: quota summary + last 5 distributions.
     */
    public PortalDashboard getDashboard(UUID tenantId, UUID memberId) {
        Member member = loadMember(memberId);
        QuotaStatus quota = complianceService.getQuotaStatus(memberId);

        // Daily usage
        LocalDate today = LocalDate.now(ZoneOffset.UTC);
        Instant dayStart = today.atStartOfDay(ZoneOffset.UTC).toInstant();
        Instant dayEnd = today.plusDays(1).atStartOfDay(ZoneOffset.UTC).toInstant();
        BigDecimal dailyUsed = distributionRepository.sumQuantityByMemberAndDay(memberId, dayStart, dayEnd);
        BigDecimal dailyLimit = ComplianceConstants.ADULT_DAILY_LIMIT_GRAMS;

        // Recent 5 distributions
        List<Distribution> recent = distributionRepository
                .findTop5ByMemberIdAndTenantIdOrderByDistributedAtDesc(memberId, tenantId);

        List<PortalDashboard.RecentDistribution> recentDtos = recent.stream()
                .map(d -> new PortalDashboard.RecentDistribution(
                        d.getDistributedAt(),
                        resolveStrainName(d.getBatchId()),
                        d.getQuantityGrams(),
                        resolveStaffName(d.getRecordedBy())
                ))
                .toList();

        return new PortalDashboard(
                member.getFirstName() + " " + member.getLastName(),
                member.getMembershipNumber(),
                quota.totalUsed(),
                quota.remaining(),
                dailyUsed,
                dailyLimit.subtract(dailyUsed),
                recentDtos
        );
    }

    /**
     * Member's own profile.
     */
    public PortalProfile getProfile(UUID tenantId, UUID memberId) {
        Member member = loadMember(memberId);
        return new PortalProfile(
                member.getFirstName(),
                member.getLastName(),
                member.getMembershipNumber(),
                member.getMembershipDate(),
                member.getStatus(),
                member.getEmail()
        );
    }

    /**
     * Detailed quota status for current month.
     */
    public PortalQuota getQuota(UUID tenantId, UUID memberId) {
        Member member = loadMember(memberId);
        QuotaStatus quota = complianceService.getQuotaStatus(memberId);

        // Daily usage
        LocalDate today = LocalDate.now(ZoneOffset.UTC);
        Instant dayStart = today.atStartOfDay(ZoneOffset.UTC).toInstant();
        Instant dayEnd = today.plusDays(1).atStartOfDay(ZoneOffset.UTC).toInstant();
        BigDecimal dailyUsed = distributionRepository.sumQuantityByMemberAndDay(memberId, dayStart, dayEnd);

        return new PortalQuota(
                quota.year(),
                quota.month(),
                dailyUsed,
                ComplianceConstants.ADULT_DAILY_LIMIT_GRAMS,
                quota.totalUsed(),
                quota.totalAllowed(),
                member.isUnder21(),
                ComplianceConstants.UNDER21_MONTHLY_LIMIT_GRAMS
        );
    }

    /**
     * Paginated distribution history for the member.
     */
    public PortalDistributionHistory getDistributionHistory(UUID tenantId, UUID memberId, Pageable pageable) {
        Page<Distribution> page = distributionRepository
                .findByMemberIdAndTenantIdOrderByDistributedAtDesc(memberId, tenantId, pageable);

        List<PortalDistributionHistory.DistributionEntry> entries = page.getContent().stream()
                .map(d -> new PortalDistributionHistory.DistributionEntry(
                        d.getDistributedAt(),
                        resolveStrainName(d.getBatchId()),
                        d.getQuantityGrams(),
                        resolveStaffName(d.getRecordedBy())
                ))
                .toList();

        return new PortalDistributionHistory(entries, page.getNumber(), page.getTotalPages(), page.getTotalElements());
    }

    private Member loadMember(UUID memberId) {
        return memberRepository.findById(memberId)
                .orElseThrow(() -> new MemberNotFoundException(memberId));
    }

    private String resolveStrainName(UUID batchId) {
        return batchRepository.findById(batchId)
                .flatMap(batch -> strainRepository.findById(batch.getStrainId()))
                .map(Strain::getName)
                .orElse("Unknown");
    }

    private String resolveStaffName(UUID userId) {
        return userRepository.findById(userId)
                .map(User::getEmail)
                .orElse("Unknown");
    }
}
