package de.assecutor.votianlt.service;

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

import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.Month;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
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 allJobs.stream()
                .map(Job::getPrice)
                .filter(price -> price != null)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

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

        for (Job job : allJobs) {
            String customer = job.getCustomerSelection();
            if (customer != null && job.getPrice() != null) {
                revenueByCustomer.merge(customer, job.getPrice(), BigDecimal::add);
            }
        }

        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();
        }
        // Trim and escape regex special characters for MongoDB
        String trimmedCustomer = customer.trim();
        String escapedCustomer = trimmedCustomer.replaceAll("([.^$*+?()\\[\\]{}|\\\\])", "\\\\$1");

        // First try exact match (with optional whitespace)
        String exactRegex = "^\\s*" + escapedCustomer + "\\s*$";
        List<Job> jobs = jobRepository.findByCustomerSelectionIgnoreCase(exactRegex);
        log.debug("getJobsByCustomer('{}') - exact regex: '{}' - found {} jobs", customer, exactRegex, jobs.size());

        // If no exact match, try partial match (customer name contains the search term)
        if (jobs.isEmpty()) {
            String containsRegex = ".*" + escapedCustomer + ".*";
            jobs = jobRepository.findByCustomerSelectionIgnoreCase(containsRegex);
            log.debug("getJobsByCustomer('{}') - contains regex: '{}' - found {} jobs", customer, containsRegex, jobs.size());
        }

        return jobs;
    }

    /**
     * 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();
    }

    // ==================== 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();
    }

    /**
     * Get job counts by status filtered by customer.
     */
    public Map<JobStatus, Long> getJobCountsByStatusForCustomer(String customer) {
        List<Job> customerJobs = getJobsByCustomer(customer);
        Map<JobStatus, Long> counts = new EnumMap<>(JobStatus.class);
        for (JobStatus status : JobStatus.values()) {
            counts.put(status, 0L);
        }
        for (Job job : customerJobs) {
            counts.computeIfPresent(job.getStatus(), (k, v) -> v + 1L);
        }
        return counts;
    }

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

    /**
     * Get total revenue for a customer.
     */
    public BigDecimal getTotalRevenueForCustomer(String customer) {
        return getJobsByCustomer(customer).stream()
                .map(Job::getPrice)
                .filter(price -> price != null)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    /**
     * Get completion rate for a customer.
     */
    public double getCompletionRateForCustomer(String customer) {
        List<Job> customerJobs = getJobsByCustomer(customer);
        if (customerJobs.isEmpty()) {
            return 0.0;
        }
        long completed = customerJobs.stream()
                .filter(j -> j.getStatus() == JobStatus.COMPLETED)
                .count();
        return (double) completed / customerJobs.size() * 100.0;
    }

    /**
     * Get monthly job counts for a customer in a specific year.
     */
    public Map<Month, Long> getMonthlyJobCountsForCustomer(int year, String customer) {
        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> customerJobs = getJobsByCustomer(customer).stream()
                .filter(j -> j.getCreatedAt() != null
                        && !j.getCreatedAt().isBefore(yearStart)
                        && !j.getCreatedAt().isAfter(yearEnd))
                .toList();

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

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

        return monthlyCounts;
    }

    /**
     * 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();
    }

    /**
     * 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();
        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;
    }

    /**
     * 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());
    }
}
