package de.nilzbu.mytimetracker.ui.view;

import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.tabs.Tab;
import com.vaadin.flow.component.tabs.Tabs;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import de.nilzbu.mytimetracker.model.TimeEntry;
import de.nilzbu.mytimetracker.model.User;
import de.nilzbu.mytimetracker.repository.UserRepository;
import de.nilzbu.mytimetracker.service.TimeEntryService;
import de.nilzbu.mytimetracker.ui.component.ChartJsComponent;
import de.nilzbu.mytimetracker.ui.layout.MainLayout;
import jakarta.annotation.security.PermitAll;
import org.springframework.security.core.context.SecurityContextHolder;

import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@PermitAll
@PageTitle("Dashboard")
@Route(value = "", layout = MainLayout.class)
public class MainView extends VerticalLayout {

    private final TimeEntryService timeEntryService;
    private final UserRepository userRepository;
    private User currentUser;

    private final ComboBox<Integer> yearSelector = new ComboBox<>("Year");
    private final ComboBox<Integer> monthSelector = new ComboBox<>("Month");
    private final ComboBox<Integer> quarterSelector = new ComboBox<>("Quarter");

    private final Div filterContainer = new Div();
    private final Div contentContainer = new Div();

    public MainView(TimeEntryService timeEntryService, UserRepository userRepository) {
        this.timeEntryService = timeEntryService;
        this.userRepository = userRepository;

        configureLayout();
        initializeCurrentUser();
        createFilterSelectors();
        renderTabsAndContent();
    }

    private void configureLayout() {
        setSizeFull();
        setPadding(false);
        setSpacing(false);
        setMargin(false);

        contentContainer.setSizeFull();
        contentContainer.getStyle().set("overflow", "auto");
        setFlexGrow(1, contentContainer);
    }

    private void initializeCurrentUser() {
        String username = SecurityContextHolder.getContext().getAuthentication().getName();
        this.currentUser = userRepository.findByUsername(username)
                .orElseThrow(() -> new RuntimeException("User not found: " + username));
    }

    private void createFilterSelectors() {
        int currentYear = LocalDate.now().getYear();
        int currentMonth = LocalDate.now().getMonthValue();
        int currentQuarter = (currentMonth - 1) / 3 + 1;

        yearSelector.setItems(IntStream.rangeClosed(2020, currentYear).boxed().toList());
        yearSelector.setValue(currentYear);

        monthSelector.setItems(IntStream.rangeClosed(1, 12).boxed().toList());
        monthSelector.setValue(currentMonth);

        quarterSelector.setItems(1, 2, 3, 4);
        quarterSelector.setValue(currentQuarter);
    }

    private void renderTabsAndContent() {
        Tabs scopeTabs = new Tabs(
                new Tab("All"),
                new Tab("Year"),
                new Tab("Quarter"),
                new Tab("Month")
        );

        scopeTabs.addSelectedChangeListener(e ->
                updateContent(scopeTabs.getSelectedTab().getLabel())
        );

        add(new H2("Dashboard"), scopeTabs, filterContainer, contentContainer);
        updateContent("All");
    }

    private void updateContent(String scope) {
        contentContainer.removeAll();
        filterContainer.removeAll();

        yearSelector.setVisible(false);
        monthSelector.setVisible(false);
        quarterSelector.setVisible(false);

        List<TimeEntry> entries = switch (scope) {
            case "Month" -> {
                configureFilterScope(yearSelector, monthSelector );
                yield timeEntryService.getEntriesForMonth(currentUser, yearSelector.getValue(), monthSelector.getValue());
            }
            case "Quarter" -> {
                configureFilterScope(yearSelector, quarterSelector);
                yield timeEntryService.getEntriesForQuarter(currentUser, yearSelector.getValue(), quarterSelector.getValue());
            }
            case "Year" -> {
                configureFilterScope(yearSelector, null);
                yield timeEntryService.getEntriesForYear(currentUser, yearSelector.getValue());
            }
            default -> timeEntryService.getEntriesForUser(currentUser);
        };

        renderCharts(entries);
    }

    private void configureFilterScope(ComboBox<?>... selectors) {
        for (ComboBox<?> selector : selectors) {
            if (selector == null) continue;
            filterContainer.add(selector);
            selector.setVisible(true);
        }

        yearSelector.addValueChangeListener(e -> updateContent("Year"));
        monthSelector.addValueChangeListener(e -> updateContent("Month"));
        quarterSelector.addValueChangeListener(e -> updateContent("Quarter"));
    }

    private void renderCharts(List<TimeEntry> scopedEntries) {
        List<TimeEntry> allEntries = timeEntryService.getEntriesForUser(currentUser);

        VerticalLayout chartLayout = new VerticalLayout();
        chartLayout.setSizeFull();
        chartLayout.setPadding(false);
        chartLayout.setSpacing(true);

        Component categoryChart = createCategoryBarChart(scopedEntries);
        Component overtimeChart = createOvertimeLineChart(allEntries, scopedEntries);

        categoryChart.getStyle().set("minHeight", "45vh");
        overtimeChart.getStyle().set("minHeight", "50vh");

        chartLayout.add(categoryChart, overtimeChart);
        chartLayout.setFlexGrow(1, categoryChart);
        chartLayout.setFlexGrow(1, overtimeChart);

        contentContainer.add(chartLayout);
    }

    private Component createCategoryBarChart(List<TimeEntry> entries) {
        Map<String, Long> statusCount = entries.stream()
                .collect(Collectors.groupingBy(e -> e.getStatus().name(), Collectors.counting()));

        return new ChartJsComponent(
                ChartJsComponent.generateBarChartData(statusCount),
                "Entries by Category");
    }

    private Component createOvertimeLineChart(List<TimeEntry> allEntries, List<TimeEntry> scopedEntries) {
        Map<LocalDate, Double> dailySaldo = new TreeMap<>();
        int accumulatedMinutes = 0;

        List<TimeEntry> sortedEntries = allEntries.stream()
                .sorted(Comparator.comparing(TimeEntry::getDate))
                .toList();

        for (TimeEntry entry : sortedEntries) {
            accumulatedMinutes += timeEntryService.calculateNetWorkMinutes(entry) - entry.getTargetMinutes();
            dailySaldo.put(entry.getDate(), accumulatedMinutes / 60.0);
        }

        List<LocalDate> scopedDates = scopedEntries.stream()
                .map(TimeEntry::getDate)
                .sorted()
                .distinct()
                .toList();

        List<Double> saldoValues = scopedDates.stream()
                .map(date -> dailySaldo.getOrDefault(date, 0.0))
                .toList();

        return new ChartJsComponent(
                ChartJsComponent.generateLineChartData(scopedDates, saldoValues),
                "Overtime Balance Over Time (in hours)");
    }
}