package com.astalange.core.entity;

import jakarta.persistence.*;
import java.time.LocalTime;
import java.time.Duration;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

@Entity
@Table(name = "creneau_entrainement")
public class CreneauEntrainement {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "categorie_id", nullable = false)
    private Categorie categorie;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "terrain_id", nullable = false)
    private Terrain terrain;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private ZoneTerrain zone;

    @Enumerated(EnumType.STRING)
    @Column(name = "jour_semaine", nullable = false)
    private JourSemaine jourSemaine;

    @Column(name = "heure_debut", nullable = false)
    private LocalTime heureDebut;

    @Column(name = "heure_fin", nullable = false)
    private LocalTime heureFin;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "saison_id", nullable = false)
    private Saison saison;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "equipe_id")
    private Equipe equipe;

    @Transient
    private boolean enConflit = false;

    @Transient
    private Set<String> categoriesEnConflit = new HashSet<>();

    @Transient
    private String initialesEducateurs = "";

    // Logic for Gantt diagram grid placement

    public int getStartColumn() {
        if (heureDebut == null) return 3;
        // Clip to working hours 08:00 to 22:00
        LocalTime time = heureDebut;
        if (time.isBefore(LocalTime.of(8, 0))) {
            time = LocalTime.of(8, 0);
        }
        long minutes = Duration.between(LocalTime.of(8, 0), time).toMinutes();
        return (int) (minutes / 15) + 3; // +3 because col 1 is Field name, col 2 is Zone name
    }

    public int getEndColumn() {
        if (heureFin == null) return 3;
        LocalTime time = heureFin;
        if (time.isAfter(LocalTime.of(22, 0))) {
            time = LocalTime.of(22, 0);
        }
        long minutes = Duration.between(LocalTime.of(8, 0), time).toMinutes();
        return (int) (minutes / 15) + 3;
    }

    public int getStartRow(int fieldIndex) {
        int baseRow = 3 + (fieldIndex * 5); // Row index matching HTML layout (base starts at 3, 5 rows per field)
        if (zone == null) return baseRow;
        return switch (zone) {
            case COMPLET, DEMI_A, QUART_1 -> baseRow;
            case QUART_2 -> baseRow + 1;
            case DEMI_B, QUART_3 -> baseRow + 2;
            case QUART_4 -> baseRow + 3;
        };
    }

    public int getEndRow(int fieldIndex) {
        int baseRow = 3 + (fieldIndex * 5);
        if (zone == null) return baseRow + 4;
        return switch (zone) {
            case QUART_1 -> baseRow + 1;
            case DEMI_A, QUART_2 -> baseRow + 2;
            case QUART_3 -> baseRow + 3;
            case DEMI_B, QUART_4, COMPLET -> baseRow + 4;
        };
    }

    // Overlap and conflict checking logic

    public boolean overlapsWith(CreneauEntrainement other) {
        if (this.id != null && this.id.equals(other.id)) {
            return false; // same slot
        }
        if (this.jourSemaine != other.jourSemaine) {
            return false;
        }
        if (this.terrain == null || other.terrain == null || !Objects.equals(this.terrain.getId(), other.terrain.getId())) {
            return false;
        }
        
        // Time overlap check: start1 < end2 and start2 < end1
        boolean timeOverlap = this.heureDebut.isBefore(other.heureFin) && other.heureDebut.isBefore(this.heureFin);
        if (!timeOverlap) {
            return false;
        }
        
        // Spatial overlap check
        if (this.zone == null || other.zone == null) {
            return false;
        }
        Set<Integer> q1 = this.zone.getQuarters();
        Set<Integer> q2 = other.zone.getQuarters();
        return !Collections.disjoint(q1, q2);
    }

    // Getters and Setters

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Categorie getCategorie() {
        return categorie;
    }

    public void setCategorie(Categorie categorie) {
        this.categorie = categorie;
    }

    public Terrain getTerrain() {
        return terrain;
    }

    public void setTerrain(Terrain terrain) {
        this.terrain = terrain;
    }

    public ZoneTerrain getZone() {
        return zone;
    }

    public void setZone(ZoneTerrain zone) {
        this.zone = zone;
    }

    public JourSemaine getJourSemaine() {
        return jourSemaine;
    }

    public void setJourSemaine(JourSemaine jourSemaine) {
        this.jourSemaine = jourSemaine;
    }

    public LocalTime getHeureDebut() {
        return heureDebut;
    }

    public void setHeureDebut(LocalTime heureDebut) {
        this.heureDebut = heureDebut;
    }

    public LocalTime getHeureFin() {
        return heureFin;
    }

    public void setHeureFin(LocalTime heureFin) {
        this.heureFin = heureFin;
    }

    public Saison getSaison() {
        return saison;
    }

    public void setSaison(Saison saison) {
        this.saison = saison;
    }

    public Equipe getEquipe() {
        return equipe;
    }

    public void setEquipe(Equipe equipe) {
        this.equipe = equipe;
    }

    public boolean isEnConflit() {
        return enConflit;
    }

    public void setEnConflit(boolean enConflit) {
        this.enConflit = enConflit;
    }

    public Set<String> getCategoriesEnConflit() {
        return categoriesEnConflit;
    }

    public void setCategoriesEnConflit(Set<String> categoriesEnConflit) {
        this.categoriesEnConflit = categoriesEnConflit;
    }

    public String getInitialesEducateurs() {
        return initialesEducateurs;
    }

    public void setInitialesEducateurs(String initialesEducateurs) {
        this.initialesEducateurs = initialesEducateurs;
    }
}
