package org.raddatz.familienarchiv.person;

import java.util.List;
import java.util.Optional;
import java.util.UUID;

import org.raddatz.familienarchiv.person.PersonSummaryDTO;
import org.raddatz.familienarchiv.person.Person;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

@Repository
public interface PersonRepository extends JpaRepository<Person, UUID> {

    @Query("SELECT DISTINCT p FROM Person p LEFT JOIN p.nameAliases a WHERE " +
           "LOWER(CONCAT(COALESCE(p.firstName, ''),' ',p.lastName)) LIKE LOWER(CONCAT('%', :query, '%')) OR " +
           "LOWER(CONCAT(p.lastName, ' ', COALESCE(p.firstName, ''))) LIKE LOWER(CONCAT('%', :query, '%')) OR " +
           "LOWER(p.alias) LIKE LOWER(CONCAT('%', :query, '%')) OR " +
           "LOWER(a.lastName) LIKE LOWER(CONCAT('%', :query, '%')) " +
           "ORDER BY p.lastName ASC, p.firstName ASC")
    List<Person> searchByName(@Param("query") String query);

    // Hilfsmethode: Alle sortiert laden (für den leeren Status)
    List<Person> findAllByOrderByLastNameAscFirstNameAsc();

    // Stammbaum-Knoten: alle Personen mit family_member = true.
    List<Person> findByFamilyMemberTrueOrderByLastNameAscFirstNameAsc();

    // Lookup by full alias string, used during ODS mass import
    Optional<Person> findByAliasIgnoreCase(String alias);

    // Exact first+last name match, used for filename-based sender lookup
    Optional<Person> findByFirstNameIgnoreCaseAndLastNameIgnoreCase(String firstName, String lastName);

    // --- PersonSummaryDTO with document count ---

    @Query(value = """
            SELECT p.id, p.title, p.first_name AS firstName, p.last_name AS lastName,
                   p.person_type AS personType,
                   p.alias, p.birth_year AS birthYear, p.death_year AS deathYear, p.notes,
                   p.family_member AS familyMember,
                   (SELECT COUNT(*) FROM documents d WHERE d.sender_id = p.id)
                   + (SELECT COUNT(*) FROM document_receivers dr WHERE dr.person_id = p.id) AS documentCount
            FROM persons p
            ORDER BY p.last_name ASC, p.first_name ASC
            """,
            nativeQuery = true)
    List<PersonSummaryDTO> findAllWithDocumentCount();

    @Query(value = """
            SELECT p.id, p.title, p.first_name AS firstName, p.last_name AS lastName,
                   p.person_type AS personType,
                   p.alias, p.birth_year AS birthYear, p.death_year AS deathYear, p.notes,
                   p.family_member AS familyMember,
                   (SELECT COUNT(*) FROM documents d WHERE d.sender_id = p.id)
                   + (SELECT COUNT(*) FROM document_receivers dr WHERE dr.person_id = p.id) AS documentCount
            FROM persons p
            LEFT JOIN person_name_aliases a ON a.person_id = p.id
            WHERE LOWER(CONCAT(COALESCE(p.first_name,''),' ',p.last_name)) LIKE LOWER(CONCAT('%',:query,'%'))
               OR LOWER(CONCAT(p.last_name,' ',COALESCE(p.first_name,''))) LIKE LOWER(CONCAT('%',:query,'%'))
               OR LOWER(p.alias) LIKE LOWER(CONCAT('%',:query,'%'))
               OR LOWER(a.last_name) LIKE LOWER(CONCAT('%',:query,'%'))
            GROUP BY p.id, p.title, p.first_name, p.last_name, p.person_type, p.alias, p.birth_year, p.death_year, p.notes, p.family_member
            ORDER BY p.last_name ASC, p.first_name ASC
            """,
            nativeQuery = true)
    List<PersonSummaryDTO> searchWithDocumentCount(@Param("query") String query);

    // --- Correspondent queries ---

    @Query(value = """
            SELECT p.* FROM persons p
            INNER JOIN (
                SELECT dr.person_id AS other_id, d.id AS doc_id
                FROM documents d
                JOIN document_receivers dr ON dr.document_id = d.id
                WHERE d.sender_id = :personId
                UNION ALL
                SELECT d.sender_id AS other_id, d.id AS doc_id
                FROM documents d
                JOIN document_receivers dr ON dr.document_id = d.id
                WHERE dr.person_id = :personId AND d.sender_id IS NOT NULL
            ) shared ON shared.other_id = p.id
            WHERE p.id != :personId
            GROUP BY p.id
            ORDER BY COUNT(DISTINCT shared.doc_id) DESC
            LIMIT 10
            """, nativeQuery = true)
    List<Person> findCorrespondents(@Param("personId") UUID personId);

    @Query(value = """
            SELECT p.* FROM persons p
            INNER JOIN (
                SELECT dr.person_id AS other_id, d.id AS doc_id
                FROM documents d
                JOIN document_receivers dr ON dr.document_id = d.id
                WHERE d.sender_id = :personId
                UNION ALL
                SELECT d.sender_id AS other_id, d.id AS doc_id
                FROM documents d
                JOIN document_receivers dr ON dr.document_id = d.id
                WHERE dr.person_id = :personId AND d.sender_id IS NOT NULL
            ) shared ON shared.other_id = p.id
            WHERE p.id != :personId
            AND (LOWER(CONCAT(COALESCE(p.first_name,''),' ',p.last_name)) LIKE LOWER(CONCAT('%',:q,'%'))
                 OR LOWER(CONCAT(p.last_name,' ',COALESCE(p.first_name,''))) LIKE LOWER(CONCAT('%',:q,'%'))
                 OR LOWER(p.alias) LIKE LOWER(CONCAT('%',:q,'%')))
            GROUP BY p.id
            ORDER BY COUNT(DISTINCT shared.doc_id) DESC
            LIMIT 10
            """, nativeQuery = true)
    List<Person> findCorrespondentsWithFilter(@Param("personId") UUID personId, @Param("q") String q);

    // --- Merge helpers (native SQL to bypass JPA entity layer) ---

    @Modifying
    @Query(value = "UPDATE documents SET sender_id = :target WHERE sender_id = :source", nativeQuery = true)
    void reassignSender(@Param("source") UUID source, @Param("target") UUID target);

    @Modifying
    @Query(value = """
            INSERT INTO document_receivers (document_id, person_id)
            SELECT document_id, :target FROM document_receivers
            WHERE person_id = :source
              AND document_id NOT IN (
                  SELECT document_id FROM document_receivers WHERE person_id = :target
              )
            """, nativeQuery = true)
    void insertMissingReceiverReference(@Param("source") UUID source, @Param("target") UUID target);

    @Modifying
    @Query(value = "DELETE FROM document_receivers WHERE person_id = :source", nativeQuery = true)
    void deleteReceiverReferences(@Param("source") UUID source);
}