package booking

import (
	"fmt"
	"time"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"

	"github.com/rjNemo/rentease/internal/service/booking"
)

type PgStore struct {
	db *gorm.DB
}

func NewPgStore(db *gorm.DB) *PgStore {
	return &PgStore{db}
}

func (ps *PgStore) All() []*booking.Line {
	bookings := make([]*booking.Line, 0)
	ps.db.Raw(`
    select bookings.id, customer_name, "from", "to", platform, sum(price * quantity) as total, canceled
    from bookings
      left join items on bookings.id = items.booking_id
    group by bookings.id
    order by id desc;
    `).
		Scan(&bookings)
	return bookings
}

func (ps *PgStore) Search(value string) []*booking.Line {
	bookings := make([]*booking.Line, 0)
	ps.db.Raw(`
    select bookings.id, customer_name, "from", "to", platform, sum(price * quantity) as total, canceled
    from bookings
      left join items on bookings.id = items.booking_id
    where customer_name ilike ?
    group by bookings.id
    order by id desc;
    `, fmt.Sprintf("%%%s%%", value)).
		Scan(&bookings)
	return bookings
}

func (ps *PgStore) List(from, to time.Time) ([]*booking.Line, error) {
	bookings := make([]*booking.Line, 0)
	if err := ps.db.Raw(`
    select bookings.id, customer_name, "from", "to", platform, sum(price * quantity) as total, platform_fees
    from bookings
      join items on bookings.id = items.booking_id
      where "to" between ? and ?
        and canceled = false
    group by bookings.id
    order by id desc;
    `, from, to).
		Scan(&bookings).Error; err != nil {
		return nil, fmt.Errorf("failed to list bookings: %w", err)
	}
	return bookings, nil
}

func (ps *PgStore) CardTotal(from, to time.Time) (float64, error) {
	var total float64
	if err := ps.db.Raw(`
    select sum(total)
    from (select sum(price * quantity) as total
      from bookings
        join items on bookings.id = items.booking_id
      where "to" between ? and ?
        and canceled = false
        and payment_method = 'Card'
      group by booking_id) as t;
    `, from, to).
		Scan(&total).Error; err != nil {
		return 0, fmt.Errorf("failed to get card total: %w", err)
	}
	return total, nil
}

func (ps *PgStore) Get(id int) *booking.Booking {
	var b booking.Booking
	ps.db.Preload("Items").Preload("Payments").First(&b, id)
	return &b
}

func (ps *PgStore) Create(b *booking.Booking) error {
	return ps.db.Create(b).Error
}

func (ps *PgStore) Update(b *booking.Booking) error {
	return ps.db.Save(b).Error
}

func (ps *PgStore) Cancel(id int) error {
	return ps.db.Model(&booking.Booking{}).Where("id = ?", id).Update("canceled", true).Error
}

// Item methods
func (ps *PgStore) CreateItem(i *booking.Item) error {
	return ps.db.Create(i).Error
}

func (ps *PgStore) PayItem(id int) (*booking.Item, error) {
	i := new(booking.Item)
	if err := ps.db.Model(i).
		Clauses(clause.Returning{}).
		Where("id = ?", id).
		Update("payment_status", "Completed").
		Error; err != nil {
		return nil, err
	}
	return i, nil
}

func (ps *PgStore) GetItem(id int) (*booking.Item, error) {
	i := &booking.Item{Id: id}
	err := ps.db.First(i).Error
	return i, err
}

func (ps *PgStore) UpdateItem(id int, item string, paymentMethod string, paymentStatus string, qty int, price float64) (*booking.Item, error) {
	i := new(booking.Item)
	err := ps.db.Model(i).
		Clauses(clause.Returning{}).
		Where("id = ?", id).
		Updates(map[string]any{
			"item":           item,
			"payment_method": paymentMethod,
			"payment_status": paymentStatus,
			"quantity":       qty,
			"price":          price,
		}).
		Error
	return i, err
}

func (ps *PgStore) CreatePayment(p *booking.Payment) (*booking.Payment, error) {
	if err := ps.db.Create(p).Error; err != nil {
		return nil, fmt.Errorf("failed to create payment: %w", err)
	}
	return p, nil
}

func (ps *PgStore) GetPayment(id int) (*booking.Payment, error) {
	p := &booking.Payment{}
	err := ps.db.First(p, id).Error
	return p, err
}

func (ps *PgStore) UpdatePayment(id int, amount float64, paymentMethod string) (*booking.Payment, error) {
	p := new(booking.Payment)
	err := ps.db.Model(p).
		Clauses(clause.Returning{}).
		Where("id = ?", id).
		Updates(map[string]any{
			"amount":         amount,
			"payment_method": paymentMethod,
		}).
		Error
	return p, err
}
