package com.example.FinanceTracker.Repo;

import java.time.LocalDate;
import java.util.List;
import java.util.UUID;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import com.example.FinanceTracker.DTOs.GroupedTransaction;
import com.example.FinanceTracker.DTOs.QuartlyTotals;
import com.example.FinanceTracker.Models.Transaction;

@Repository
public interface TransactionRepo extends JpaRepository<Transaction, UUID> {

        @Query(value = """
                        SELECT *
                        FROM transactions
                        WHERE date BETWEEN :fromDate AND :toDate
                        ORDER BY date
                        """, nativeQuery = true)
        Page<Transaction> getTransactionsBetween(Pageable pageable, @Param("fromDate") LocalDate fromDate,
                        @Param("toDate") LocalDate toDate);

        @Query(value = "SELECT * FROM transactions ORDER BY date DESC", nativeQuery = true)
        Page<Transaction> findAllPaged(Pageable pageable);

        @Query(value = """
                        SELECT * FROM transactions
                        WHERE CONCAT_WS('', date, description, category, money_in, money_out, fee, balance)
                            LIKE Concat('%', :searchValue, '%')
                        ORDER BY date
                        """, nativeQuery = true)
        Page<Transaction> searchTransactions(Pageable pageable, @Param("searchValue") String SearchValue);

        @Query(value = """
                        SELECT (balance - 30) as balance 
                        FROM transactions ORDER BY date DESC LIMIT 1
                        """, nativeQuery = true)
        double getLatestBalance();

        // =============================================================
        // MONEY IN QUERIES
        // =============================================================

        @Query(value = """
                        SELECT SUM(money_in)
                        FROM transactions
                        ORDER BY date
                        """, nativeQuery = true)
        double getTotalMoneyIn();

        @Query(value = """
                        SELECT SUM(money_in)
                        FROM transactions
                        WHERE YEAR(date) = :year
                        ORDER BY date
                        """, nativeQuery = true)
        double getTotalMoneyInByYear(@Param("year") int year);

        @Query(value = """
                        SELECT SUM(money_in) / (SELECT COUNT(DISTINCT MONTH(date)) FROM transactions WHERE YEAR(date) = :year)
                        FROM transactions
                        WHERE YEAR(date) = :year
                        """, nativeQuery = true)
        double getAverageMonthlyMoneyIn(int year);

        @Query(value = """
                        SELECT category, SUM(money_in)
                        FROM transactions
                        WHERE money_in != 0
                        GROUP BY category
                        """, nativeQuery = true)
        List<GroupedTransaction> getIncomeGroupedTransactions();

        // =============================================================
        // MONEY OUT QUERIES
        // =============================================================
        @Query(value = """
                        SELECT SUM(money_out)
                        FROM transactions
                        ORDER BY date
                        """, nativeQuery = true)
        double getTotalMoneyOut();

        @Query(value = """
                        SELECT SUM(money_out) / (SELECT COUNT(DISTINCT MONTH(date)) FROM transactions WHERE YEAR(date) = :year)
                        FROM transactions
                        WHERE YEAR(date) = :year
                        """, nativeQuery = true)
        double getAverageMonthlyMoneyOut(int year);

        @Query(value = """
                        SELECT category, SUM(money_out)
                        FROM transactions
                        WHERE money_out != 0
                        GROUP BY category
                        """, nativeQuery = true)
        List<GroupedTransaction> getExpenditureGroupedTransactions();

        // =============================================================
        // FEE QUERIES
        // =============================================================
        @Query(value = """
                        SELECT SUM(fee)
                        FROM transactions
                        ORDER BY date
                        """, nativeQuery = true)
        double getTotalFees();

        @Query(value = """
                        SELECT SUM(fee)
                        FROM transactions
                        WHERE YEAR(date) = :year
                        ORDER BY date
                        """, nativeQuery = true)
        double getTotalFeesByYear(@Param("year") int year);

        // =============================================================
        // INTEREST QUERIES
        // =============================================================
        @Query(value = """
                        SELECT SUM(money_in)
                        FROM transactions
                        WHERE category = 'Interest'
                        """, nativeQuery = true)
        double getTotalInterest();

        @Query(value = """
                        SELECT SUM(money_in) / (SELECT COUNT(DISTINCT MONTH(date)) FROM transactions WHERE YEAR(date) = :year)
                        FROM transactions
                        WHERE YEAR(date) = :year AND category = 'Interest'
                               """, nativeQuery = true)
        double averageMonthlyInterest(@Param("year") int year);

        // =============================================================
        // OTHER QUERIES
        // =============================================================
        @Query(value = """
                        SELECT
                            CONCAT('Q', QUARTER(date)) AS Quarters,
                            SUM(money_in) AS \"Total MoneyIn\",
                            SUM(money_out) AS \"Total MoneyOut\",
                            SUM(fee) AS \"Total Fees\"
                        FROM transactions
                        WHERE YEAR(date) = :year
                        GROUP BY QUARTER(date)
                        ORDER BY date
                        """, nativeQuery = true)
        List<QuartlyTotals> getQuarterlyTotals(@Param("year") int year);

        @Query(value = """
                        SELECT
                                (SELECT SUM(money_in) FROM transactions WHERE category != 'Interest' AND money_in != 0) AS money_in,
                                (SELECT SUM(money_out) * -1 FROM transactions WHERE category != 'Fees' AND money_out != 0) AS money_out,
                                (SELECT SUM(money_in) FROM transactions WHERE category = 'Interest') AS Interest,
                                (SELECT SUM(fee) * -1 FROM transactions) AS fees
                        FROM transactions LIMIT 1
                        """, nativeQuery = true)
        List<Object[]> getTotals();

        @Query(value = """
                        SELECT (SUM(money_in) + SUM(money_out)) AS "Net Cashflow"
                        FROM transactions
                        """, nativeQuery = true)
        double getNetCashflow();

        @Query(value = """
                        SELECT (SUM(money_in) + SUM(money_out)) / NULLIF(SUM(money_in), 0) AS "Savings Rate"
                        FROM transactions;
                        """, nativeQuery = true)
        double getSavingRate();

}
