package com.yaoai.admin.dashboard;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yaoai.admin.dashboard.dto.DashboardOverview;
import com.yaoai.admin.dashboard.dto.DashboardProviderStats;
import com.yaoai.admin.dashboard.dto.DashboardTaskStats;
import com.yaoai.admin.dashboard.dto.DashboardTrendPoint;
import com.yaoai.domain.entity.AiTask;
import com.yaoai.domain.entity.BillingRecord;
import com.yaoai.domain.entity.Tenant;
import com.yaoai.domain.entity.User;
import com.yaoai.domain.mapper.AiTaskMapper;
import com.yaoai.domain.mapper.BillingRecordMapper;
import com.yaoai.domain.mapper.TenantMapper;
import com.yaoai.domain.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class DashboardService {

    private final TenantMapper tenantMapper;
    private final UserMapper userMapper;
    private final BillingRecordMapper billingRecordMapper;
    private final AiTaskMapper aiTaskMapper;

    @Value("${volcengine.ark.text-model:${VOLCENGINE_ARK_MODEL:doubao-seed-2-0-code-preview-260215}}")
    private String textModelId;

    @Value("${volcengine.ark.image-model:doubao-seedream-5-0-260128}")
    private String imageModelId;

    @Value("${volcengine.ark.video-model:doubao-seedance-2-0-fast-260128}")
    private String videoModelId;

    public DashboardOverview overview() {
        LocalDateTime todayStart = LocalDate.now().atStartOfDay();
        List<BillingRecord> todayRecords = billingRecordMapper.selectList(Wrappers.<BillingRecord>lambdaQuery()
                .ge(BillingRecord::getCreatedAt, todayStart));

        BigDecimal consumed = todayRecords.stream()
                .filter(this::isConsume)
                .map(BillingRecord::getCredits)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal recharged = todayRecords.stream()
                .filter(this::isRecharge)
                .map(BillingRecord::getCredits)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        return DashboardOverview.builder()
                .totalTenants(countActiveRows())
                .activeTenants(tenantMapper.selectCount(Wrappers.<Tenant>lambdaQuery()
                        .eq(Tenant::getStatus, 1)
                        .isNull(Tenant::getDeletedAt)))
                .totalUsers(userMapper.selectCount(Wrappers.<User>lambdaQuery().isNull(User::getDeletedAt)))
                .todayNewTenants(tenantMapper.selectCount(Wrappers.<Tenant>lambdaQuery()
                        .ge(Tenant::getCreatedAt, todayStart)
                        .isNull(Tenant::getDeletedAt)))
                .todayConsumedCredits(consumed)
                .todayRechargedCredits(recharged)
                .build();
    }

    public List<DashboardTrendPoint> trends(int days) {
        int safeDays = Math.max(1, Math.min(days, 90));
        LocalDate startDay = LocalDate.now().minusDays(safeDays - 1L);
        LocalDateTime start = startDay.atStartOfDay();

        List<Tenant> tenants = tenantMapper.selectList(Wrappers.<Tenant>lambdaQuery()
                .ge(Tenant::getCreatedAt, start)
                .isNull(Tenant::getDeletedAt));
        List<BillingRecord> records = billingRecordMapper.selectList(Wrappers.<BillingRecord>lambdaQuery()
                .ge(BillingRecord::getCreatedAt, start));

        Map<LocalDate, Long> tenantCounts = tenants.stream()
                .filter(t -> t.getCreatedAt() != null)
                .collect(Collectors.groupingBy(t -> t.getCreatedAt().toLocalDate(), Collectors.counting()));

        Map<LocalDate, BigDecimal> consumed = new LinkedHashMap<>();
        Map<LocalDate, BigDecimal> recharged = new LinkedHashMap<>();
        for (BillingRecord record : records) {
            if (record.getCreatedAt() == null || record.getCredits() == null) {
                continue;
            }
            LocalDate day = record.getCreatedAt().toLocalDate();
            if (isRecharge(record)) {
                recharged.merge(day, record.getCredits(), BigDecimal::add);
            } else if (isSuccessfulConsume(record)) {
                consumed.merge(day, record.getCredits(), BigDecimal::add);
            }
        }

        List<DashboardTrendPoint> points = new ArrayList<>();
        for (int i = 0; i < safeDays; i++) {
            LocalDate day = startDay.plusDays(i);
            points.add(DashboardTrendPoint.builder()
                    .day(day)
                    .newTenants(tenantCounts.getOrDefault(day, 0L))
                    .consumedCredits(consumed.getOrDefault(day, BigDecimal.ZERO))
                    .rechargedCredits(recharged.getOrDefault(day, BigDecimal.ZERO))
                    .build());
        }
        return points;
    }

    public List<DashboardTaskStats> tasks() {
        return aiTaskMapper.selectList(Wrappers.emptyWrapper()).stream()
                .collect(Collectors.groupingBy(task -> blankToDefault(task.getTaskType(), "unknown")))
                .entrySet().stream()
                .map(entry -> toTaskStats(entry.getKey(), entry.getValue()))
                .sorted(Comparator.comparing(DashboardTaskStats::getTotal).reversed())
                .toList();
    }

    public List<DashboardProviderStats> providers() {
        return billingRecordMapper.selectList(Wrappers.<BillingRecord>lambdaQuery()
                        .isNotNull(BillingRecord::getModelProvider)
                        .eq(BillingRecord::getSuccess, true)
                        .gt(BillingRecord::getCredits, BigDecimal.ZERO))
                .stream()
                .filter(this::isSuccessfulConsume)
                .filter(this::isIntegratedArkModel)
                .collect(Collectors.groupingBy(this::providerKey))
                .entrySet().stream()
                .map(entry -> toProviderStats(entry.getValue()))
                .filter(item -> item.getCredits() != null && item.getCredits().compareTo(BigDecimal.ZERO) > 0)
                .sorted(Comparator.comparing(DashboardProviderStats::getCredits).reversed())
                .toList();
    }

    private long countActiveRows() {
        return tenantMapper.selectCount(Wrappers.<Tenant>lambdaQuery().isNull(Tenant::getDeletedAt));
    }

    private DashboardTaskStats toTaskStats(String taskType, List<AiTask> tasks) {
        long total = tasks.size();
        long succeeded = tasks.stream().filter(t -> "succeeded".equals(t.getStatus())).count();
        long failed = tasks.stream().filter(t -> "failed".equals(t.getStatus())).count();
        return DashboardTaskStats.builder()
                .taskType(taskType)
                .total(total)
                .succeeded(succeeded)
                .failed(failed)
                .successRate(total == 0 ? 0 : succeeded * 1.0 / total)
                .build();
    }

    private DashboardProviderStats toProviderStats(List<BillingRecord> records) {
        BillingRecord first = records.get(0);
        long total = records.size();
        long succeeded = records.stream().filter(r -> !Boolean.FALSE.equals(r.getSuccess())).count();
        long failed = total - succeeded;
        BigDecimal credits = records.stream()
                .map(BillingRecord::getCredits)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        return DashboardProviderStats.builder()
                .modelProvider(first.getModelProvider())
                .modelId(first.getModelId())
                .modelName(first.getModelName())
                .total(total)
                .succeeded(succeeded)
                .failed(failed)
                .credits(credits)
                .failureRate(total == 0 ? 0 : failed * 1.0 / total)
                .build();
    }

    private String providerKey(BillingRecord record) {
        return blankToDefault(record.getModelProvider(), "unknown") + "|"
                + blankToDefault(record.getModelId(), "unknown") + "|"
                + blankToDefault(record.getModelName(), "unknown");
    }

    private boolean isConsume(BillingRecord record) {
        return !isRecharge(record);
    }

    private boolean isSuccessfulConsume(BillingRecord record) {
        return isConsume(record)
                && Boolean.TRUE.equals(record.getSuccess())
                && record.getCredits() != null
                && record.getCredits().compareTo(BigDecimal.ZERO) > 0;
    }

    private boolean isIntegratedArkModel(BillingRecord record) {
        if (!"volcengine".equals(record.getModelProvider())) {
            return false;
        }
        Set<String> integratedModelIds = Set.of(
                textModelId,
                imageModelId,
                videoModelId);
        return integratedModelIds.contains(record.getModelId());
    }

    private boolean isRecharge(BillingRecord record) {
        return "RECHARGE".equals(record.getModality()) || "recharge".equals(record.getOperation());
    }

    private String blankToDefault(String value, String defaultValue) {
        return value == null || value.isBlank() ? defaultValue : value;
    }
}
