package de.assecutor.votianlt.service;

import de.assecutor.votianlt.model.Job;
import de.assecutor.votianlt.model.JobStatus;
import de.assecutor.votianlt.model.task.BaseTask;
import de.assecutor.votianlt.repository.JobRepository;
import de.assecutor.votianlt.repository.TaskRepository;
import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Service;

import org.bson.types.ObjectId;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.Month;
import java.util.Comparator;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * Service for job statistics and aggregations. Provides data for MCP tools and
 * reporting.
 */
@Service
@Slf4j
public class JobStatisticsService {

    private final JobRepository jobRepository;
    private final TaskRepository taskRepository;

    public JobStatisticsService(JobRepository jobRepository, TaskRepository taskRepository) {
        this.jobRepository = jobRepository;
        this.taskRepository = taskRepository;
    }

    /**
     * Get job counts grouped by status.
     */
    public Map<JobStatus, Long> getJobCountsByStatus() {
        Map<JobStatus, Long> counts = new EnumMap<>(JobStatus.class);
        for (JobStatus status : JobStatus.values()) {
            counts.put(status, jobRepository.countByStatus(status));
        }
        return counts;
    }

    /**
     * Get total number of jobs.
     */
    public long getTotalJobCount() {
        return jobRepository.count();
    }

    /**
     * Get jobs created within a date range.
     */
    public List<Job> getJobsByDateRange(LocalDateTime start, LocalDateTime end) {
        return jobRepository.findByCreatedAtBetween(start, end);
    }

    /**
     * Get count of jobs created within a date range.
     */
    public long getJobCountByDateRange(LocalDateTime start, LocalDateTime end) {
        return jobRepository.findByCreatedAtBetween(start, end).size();
    }

    /**
     * Calculate completion rate (completed jobs / total jobs).
     */
    public double getCompletionRate() {
        long total = jobRepository.count();
        if (total == 0) {
            return 0.0;
        }
        long completed = jobRepository.countByStatus(JobStatus.COMPLETED);
        return (double) completed / total * 100.0;
    }

    /**
     * Get total revenue from all jobs.
     */
    public BigDecimal getTotalRevenue() {
        List<Job> allJobs = jobRepository.findAll();
        return sumRevenue(allJobs);
    }

    /**
     * Get revenue grouped by customer.
     */
    public Map<String, BigDecimal> getRevenueByCustomer() {
        Map<String, BigDecimal> revenueByCustomer = new HashMap<>();
        List<Job> allJobs = jobRepository.findAll();

        mergeRevenueByCustomer(revenueByCustomer, allJobs);
        return revenueByCustomer;
    }

    /**
     * Get top customers by revenue.
     */
    public List<Map.Entry<String, BigDecimal>> getTopCustomersByRevenue(int limit) {
        return getRevenueByCustomer().entrySet().stream().sorted((a, b) -> b.getValue().compareTo(a.getValue()))
                .limit(limit).toList();
    }

    /**
     * Get monthly job counts for a specific year.
     */
    public Map<Month, Long> getMonthlyJobCounts(int year) {
        Map<Month, Long> monthlyCounts = new LinkedHashMap<>();
        LocalDateTime yearStart = LocalDateTime.of(year, 1, 1, 0, 0);
        LocalDateTime yearEnd = LocalDateTime.of(year, 12, 31, 23, 59, 59);

        List<Job> yearJobs = jobRepository.findByCreatedAtBetween(yearStart, yearEnd);

        // Initialize all months with 0
        for (Month month : Month.values()) {
            monthlyCounts.put(month, 0L);
        }

        // Count jobs per month
        for (Job job : yearJobs) {
            if (job.getCreatedAt() != null) {
                Month month = job.getCreatedAt().getMonth();
                monthlyCounts.computeIfPresent(month, (k, v) -> v + 1L);
            }
        }

        return monthlyCounts;
    }

    /**
     * Get jobs by customer selection (case-insensitive, flexible matching).
     */
    public List<Job> getJobsByCustomer(String customer) {
        if (customer == null || customer.isBlank()) {
            return List.of();
        }
        return filterJobsByCustomer(jobRepository.findAll(), customer);
    }

    /**
     * Get task completion statistics.
     */
    public Map<String, Long> getTaskCompletionStats() {
        Map<String, Long> stats = new HashMap<>();
        stats.put("completed", taskRepository.countByCompleted(true));
        stats.put("pending", taskRepository.countByCompleted(false));
        stats.put("total", taskRepository.count());
        return stats;
    }

    /**
     * Get jobs by status.
     */
    public List<Job> getJobsByStatus(JobStatus status) {
        return jobRepository.findByStatus(status);
    }

    /**
     * Get jobs assigned to an app user.
     */
    public List<Job> getJobsByAppUser(String appUser) {
        return jobRepository.findByAppUser(appUser);
    }

    /**
     * Get job by job number.
     */
    public Job getJobByNumber(String jobNumber) {
        return jobRepository.findByJobNumber(jobNumber).orElse(null);
    }

    /**
     * Get jobs by pickup city.
     */
    public List<Job> getJobsByPickupCity(String city) {
        return jobRepository.findByPickupCity(city);
    }

    /**
     * Get jobs by delivery city.
     */
    public List<Job> getJobsByDeliveryCity(String city) {
        return jobRepository.findByDeliveryCity(city);
    }

    /**
     * Get latest jobs.
     */
    public List<Job> getLatestJobs(int limit) {
        return jobRepository.findLatestJobs().stream().limit(limit).toList();
    }

    public Map<JobStatus, Long> getJobCountsByStatusForUser(String createdBy) {
        return countJobsByStatus(getJobsCreatedByUser(createdBy));
    }

    public long getTotalJobCountForUser(String createdBy) {
        return getJobsCreatedByUser(createdBy).size();
    }

    public double getCompletionRateForUser(String createdBy) {
        List<Job> userJobs = getJobsCreatedByUser(createdBy);
        return calculateCompletionRate(userJobs);
    }

    public BigDecimal getTotalRevenueForUser(String createdBy) {
        return sumRevenue(getJobsCreatedByUser(createdBy));
    }

    public BigDecimal getTotalRevenueForUserInRange(String createdBy, LocalDateTime start, LocalDateTime end) {
        return sumRevenue(filterJobsByDateRange(getJobsCreatedByUser(createdBy), start, end));
    }

    public long getTotalJobCountForUserInRange(String createdBy, LocalDateTime start, LocalDateTime end) {
        return filterJobsByDateRange(getJobsCreatedByUser(createdBy), start, end).size();
    }

    public List<Map.Entry<String, BigDecimal>> getTopCustomersByRevenueForUser(String createdBy, int limit) {
        return getRevenueByCustomerForUser(createdBy).entrySet().stream()
                .sorted((a, b) -> b.getValue().compareTo(a.getValue())).limit(limit).toList();
    }

    public List<Map.Entry<String, BigDecimal>> getTopCustomersByRevenueForUserInRange(String createdBy,
            LocalDateTime start, LocalDateTime end, int limit) {
        Map<String, BigDecimal> revenueByCustomer = new HashMap<>();
        mergeRevenueByCustomer(revenueByCustomer, filterJobsByDateRange(getJobsCreatedByUser(createdBy), start, end));
        return revenueByCustomer.entrySet().stream().sorted((a, b) -> b.getValue().compareTo(a.getValue())).limit(limit)
                .toList();
    }

    public Map<String, BigDecimal> getRevenueByCustomerForUser(String createdBy) {
        Map<String, BigDecimal> revenueByCustomer = new HashMap<>();
        mergeRevenueByCustomer(revenueByCustomer, getJobsCreatedByUser(createdBy));
        return revenueByCustomer;
    }

    public Map<Month, Long> getMonthlyJobCountsForUser(int year, String createdBy) {
        return buildMonthlyJobCounts(filterJobsByYear(getJobsCreatedByUser(createdBy), year));
    }

    public List<Job> getJobsByCustomerForUser(String createdBy, String customer) {
        return filterJobsByCustomer(getJobsCreatedByUser(createdBy), customer);
    }

    public Map<String, Long> getTaskCompletionStatsForUser(String createdBy) {
        return buildTaskCompletionStats(getJobsCreatedByUser(createdBy));
    }

    public List<Job> getJobsByStatusForUser(String createdBy, JobStatus status) {
        return getJobsCreatedByUser(createdBy).stream().filter(job -> job.getStatus() == status).toList();
    }

    public List<Job> getLatestJobsForUser(String createdBy, int limit) {
        return getJobsCreatedByUser(createdBy).stream()
                .sorted(Comparator.comparing(Job::getCreatedAt, Comparator.nullsLast(Comparator.reverseOrder())))
                .limit(limit).toList();
    }

    // ==================== Filtered Statistics Methods ====================

    /**
     * Get all available customer names for autocomplete/filtering.
     */
    public List<String> getAllCustomerNames() {
        return jobRepository.findAll().stream().map(Job::getCustomerSelection).filter(c -> c != null && !c.isBlank())
                .distinct().sorted().toList();
    }

    public List<String> getAllCustomerNamesForUser(String createdBy) {
        return getJobsCreatedByUser(createdBy).stream().map(Job::getCustomerSelection)
                .filter(customer -> customer != null && !customer.isBlank()).distinct().sorted().toList();
    }

    /**
     * Get job counts by status filtered by customer.
     */
    public Map<JobStatus, Long> getJobCountsByStatusForCustomer(String customer) {
        List<Job> customerJobs = getJobsByCustomer(customer);
        return countJobsByStatus(customerJobs);
    }

    public Map<JobStatus, Long> getJobCountsByStatusForCustomerForUser(String createdBy, String customer) {
        return countJobsByStatus(getJobsByCustomerForUser(createdBy, customer));
    }

    /**
     * Get total job count for a customer.
     */
    public long getTotalJobCountForCustomer(String customer) {
        return getJobsByCustomer(customer).size();
    }

    public long getTotalJobCountForCustomerForUser(String createdBy, String customer) {
        return getJobsByCustomerForUser(createdBy, customer).size();
    }

    public long getTotalJobCountForCustomerForUserInRange(String createdBy, String customer, LocalDateTime start,
            LocalDateTime end) {
        return filterJobsByDateRange(getJobsByCustomerForUser(createdBy, customer), start, end).size();
    }

    /**
     * Get total revenue for a customer.
     */
    public BigDecimal getTotalRevenueForCustomer(String customer) {
        return sumRevenue(getJobsByCustomer(customer));
    }

    public BigDecimal getTotalRevenueForCustomerForUser(String createdBy, String customer) {
        return sumRevenue(getJobsByCustomerForUser(createdBy, customer));
    }

    public BigDecimal getTotalRevenueForCustomerForUserInRange(String createdBy, String customer, LocalDateTime start,
            LocalDateTime end) {
        return sumRevenue(filterJobsByDateRange(getJobsByCustomerForUser(createdBy, customer), start, end));
    }

    /**
     * Get completion rate for a customer.
     */
    public double getCompletionRateForCustomer(String customer) {
        return calculateCompletionRate(getJobsByCustomer(customer));
    }

    public double getCompletionRateForCustomerForUser(String createdBy, String customer) {
        return calculateCompletionRate(getJobsByCustomerForUser(createdBy, customer));
    }

    /**
     * Get monthly job counts for a customer in a specific year.
     */
    public Map<Month, Long> getMonthlyJobCountsForCustomer(int year, String customer) {
        return buildMonthlyJobCounts(filterJobsByYear(getJobsByCustomer(customer), year));
    }

    public Map<Month, Long> getMonthlyJobCountsForCustomerForUser(int year, String createdBy, String customer) {
        return buildMonthlyJobCounts(filterJobsByYear(getJobsByCustomerForUser(createdBy, customer), year));
    }

    /**
     * Get jobs filtered by customer and optionally by status.
     */
    public List<Job> getJobsByCustomerAndStatus(String customer, JobStatus status) {
        List<Job> customerJobs = getJobsByCustomer(customer);
        if (status == null) {
            return customerJobs;
        }
        return customerJobs.stream().filter(j -> j.getStatus() == status).toList();
    }

    public List<Job> getJobsByCustomerAndStatusForUser(String createdBy, String customer, JobStatus status) {
        List<Job> customerJobs = getJobsByCustomerForUser(createdBy, customer);
        if (status == null) {
            return customerJobs;
        }
        return customerJobs.stream().filter(job -> job.getStatus() == status).toList();
    }

    /**
     * Find best matching customer name from query (fuzzy matching).
     */
    public String findMatchingCustomer(String query) {
        if (query == null || query.isBlank()) {
            return null;
        }

        String lowerQuery = query.toLowerCase();
        List<String> allCustomers = getAllCustomerNames();
        return findMatchingCustomer(query, lowerQuery, allCustomers);
    }

    public String findMatchingCustomerForUser(String createdBy, String query) {
        if (query == null || query.isBlank()) {
            return null;
        }

        String lowerQuery = query.toLowerCase();
        List<String> allCustomers = getAllCustomerNamesForUser(createdBy);
        return findMatchingCustomer(query, lowerQuery, allCustomers);
    }

    private String findMatchingCustomer(String query, String lowerQuery, List<String> allCustomers) {
        log.debug("findMatchingCustomer - Query: '{}', Available customers: {}", query, allCustomers);

        // First: exact match (case insensitive)
        for (String customer : allCustomers) {
            if (customer.equalsIgnoreCase(query)) {
                log.debug("findMatchingCustomer - Exact match found: '{}'", customer);
                return customer;
            }
        }

        // Second: check if query contains a customer name
        for (String customer : allCustomers) {
            String lowerCustomer = customer.toLowerCase();
            if (lowerQuery.contains(lowerCustomer)) {
                log.debug("findMatchingCustomer - Query contains customer: '{}'", customer);
                return customer;
            }
        }

        // Third: extract potential customer name from query and check if it matches a
        // customer
        // This handles cases like "cAPPacity GmbH" matching "cAPPacity GmbH & Co. KG"
        String extractedName = extractCustomerNameFromQuery(query);
        if (extractedName != null) {
            String lowerExtracted = extractedName.toLowerCase();
            for (String customer : allCustomers) {
                String lowerCustomer = customer.toLowerCase();
                // Check if customer name starts with the extracted name, or contains it
                if (lowerCustomer.startsWith(lowerExtracted) || lowerCustomer.contains(lowerExtracted)) {
                    log.debug("findMatchingCustomer - Extracted name '{}' matches customer: '{}'", extractedName,
                            customer);
                    return customer;
                }
            }
        }

        // Fourth: word match (any significant word from the query matches customer
        // name)
        String[] queryWords = lowerQuery.split("\\s+");
        for (String customer : allCustomers) {
            String lowerCustomer = customer.toLowerCase();
            for (String word : queryWords) {
                // Skip common words and short words
                if (word.length() >= 4 && !isCommonWord(word) && lowerCustomer.contains(word)) {
                    log.debug("findMatchingCustomer - Word match found: '{}' in '{}'", word, customer);
                    return customer;
                }
            }
        }

        log.debug("findMatchingCustomer - No match found for query: '{}'", query);
        return null;
    }

    private List<Job> getJobsCreatedByUser(String createdBy) {
        if (createdBy == null || createdBy.isBlank()) {
            return List.of();
        }
        return jobRepository.findByCreatedBy(createdBy);
    }

    private Map<JobStatus, Long> countJobsByStatus(List<Job> jobs) {
        Map<JobStatus, Long> counts = new EnumMap<>(JobStatus.class);
        for (JobStatus status : JobStatus.values()) {
            counts.put(status, 0L);
        }
        for (Job job : jobs) {
            if (job.getStatus() != null) {
                counts.computeIfPresent(job.getStatus(), (key, value) -> value + 1L);
            }
        }
        return counts;
    }

    private BigDecimal sumRevenue(List<Job> jobs) {
        return jobs.stream().map(Job::getPrice).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    private void mergeRevenueByCustomer(Map<String, BigDecimal> revenueByCustomer, List<Job> jobs) {
        for (Job job : jobs) {
            String customer = job.getCustomerSelection();
            if (customer != null && job.getPrice() != null) {
                revenueByCustomer.merge(customer, job.getPrice(), BigDecimal::add);
            }
        }
    }

    private double calculateCompletionRate(List<Job> jobs) {
        if (jobs.isEmpty()) {
            return 0.0;
        }
        long completed = jobs.stream().filter(job -> job.getStatus() == JobStatus.COMPLETED).count();
        return (double) completed / jobs.size() * 100.0;
    }

    private List<Job> filterJobsByYear(List<Job> jobs, int year) {
        LocalDateTime yearStart = LocalDateTime.of(year, 1, 1, 0, 0);
        LocalDateTime yearEnd = LocalDateTime.of(year, 12, 31, 23, 59, 59);
        return jobs.stream().filter(job -> job.getCreatedAt() != null && !job.getCreatedAt().isBefore(yearStart)
                && !job.getCreatedAt().isAfter(yearEnd)).toList();
    }

    private Map<Month, Long> buildMonthlyJobCounts(List<Job> jobs) {
        Map<Month, Long> monthlyCounts = new LinkedHashMap<>();
        for (Month month : Month.values()) {
            monthlyCounts.put(month, 0L);
        }
        for (Job job : jobs) {
            if (job.getCreatedAt() != null) {
                monthlyCounts.computeIfPresent(job.getCreatedAt().getMonth(), (key, value) -> value + 1L);
            }
        }
        return monthlyCounts;
    }

    private List<Job> filterJobsByDateRange(List<Job> jobs, LocalDateTime start, LocalDateTime end) {
        return jobs.stream().filter(job -> job.getCreatedAt() != null && !job.getCreatedAt().isBefore(start)
                && !job.getCreatedAt().isAfter(end)).toList();
    }

    private List<Job> filterJobsByCustomer(List<Job> jobs, String customer) {
        if (customer == null || customer.isBlank()) {
            return List.of();
        }

        String normalizedCustomer = customer.trim();
        List<Job> exactMatches = jobs.stream().filter(job -> job.getCustomerSelection() != null
                && job.getCustomerSelection().trim().equalsIgnoreCase(normalizedCustomer)).toList();
        if (!exactMatches.isEmpty()) {
            log.debug("filterJobsByCustomer('{}') - exact match found: {}", customer, exactMatches.size());
            return exactMatches;
        }

        String lowerCustomer = normalizedCustomer.toLowerCase();
        List<Job> partialMatches = jobs.stream().filter(job -> job.getCustomerSelection() != null
                && job.getCustomerSelection().toLowerCase().contains(lowerCustomer)).toList();
        log.debug("filterJobsByCustomer('{}') - partial match found: {}", customer, partialMatches.size());
        return partialMatches;
    }

    private Map<String, Long> buildTaskCompletionStats(List<Job> jobs) {
        Map<String, Long> stats = new HashMap<>();
        stats.put("completed", 0L);
        stats.put("pending", 0L);
        stats.put("total", 0L);

        List<ObjectId> jobIds = jobs.stream().map(Job::getId).filter(Objects::nonNull).toList();
        if (jobIds.isEmpty()) {
            return stats;
        }

        List<BaseTask> tasks = taskRepository.findByJobIdIn(jobIds);
        long completed = tasks.stream().filter(BaseTask::isCompleted).count();
        long total = tasks.size();

        stats.put("completed", completed);
        stats.put("pending", total - completed);
        stats.put("total", total);
        return stats;
    }

    /**
     * Extract potential customer name from a query string. Looks for patterns like
     * "firma X", "kunde X", "für X", etc.
     */
    private String extractCustomerNameFromQuery(String query) {
        String lowerQuery = query.toLowerCase();

        // Patterns that typically precede a customer name
        String[] patterns = { "firma ", "kunde ", "kunden ", "unternehmen ", "für die firma ", "für den kunden ",
                "von der firma ", "vom kunden ", "der firma ", "des kunden ", "bei " };

        for (String pattern : patterns) {
            int idx = lowerQuery.indexOf(pattern);
            if (idx >= 0) {
                // Extract everything after the pattern
                int startIdx = idx + pattern.length();
                String afterPattern = query.substring(startIdx).trim();

                // Remove common trailing words
                afterPattern = removeTrailingCommonWords(afterPattern);

                if (!afterPattern.isBlank()) {
                    log.debug("extractCustomerNameFromQuery - Found potential name: '{}'", afterPattern);
                    return afterPattern;
                }
            }
        }

        return null;
    }

    /**
     * Remove common trailing words from extracted customer name.
     */
    private String removeTrailingCommonWords(String text) {
        String[] trailingPatterns = { " an$", " anzeigen$", " zeigen$", " auflisten$", " liste$", " status$",
                " mit status$", " die$", " der$", " das$" };

        String result = text;
        for (String pattern : trailingPatterns) {
            result = result.replaceAll("(?i)" + pattern, "").trim();
        }

        return result;
    }

    /**
     * Check if a word is a common German/English word that should be ignored in
     * matching.
     */
    private boolean isCommonWord(String word) {
        return Set.of("zeige", "alle", "jobs", "der", "die", "das", "für", "von", "mit", "und", "firma", "kunde",
                "status", "welche", "sind", "gmbh", "show", "all", "the").contains(word.toLowerCase());
    }
}
