package db

import (
	"context"
	"database/sql"
	"fmt"
)

type GetTopListLocationsParams struct {
	Limit      int32
	Offset     int32
	OrderBy    string
	RegionType sql.NullInt16
}

type GetTopListLocationsRow struct {
	RowNumber      int32          `json:"row_number"`
	ID             int32          `json:"id"`
	Name           string         `json:"name"`
	RegionName     string         `json:"region_name"`
	Thumbnail      sql.NullString `json:"thumbnail"`
	Address        string         `json:"address"`
	GoogleMapsLink string         `json:"google_maps_link"`
	RegencyName    string         `json:"regency_name"`
	CriticScore    int16          `json:"critic_score"`
	CriticCount    int16          `json:"critic_count"`
	UserScore      int16          `json:"user_score"`
	UserCount      int16          `json:"user_count"`
	TotalCount     int16          `json:"total_count"`
	CriticBayes    int16          `json:"critic_bayes"`
	UserBayes      int16          `json:"user_bayes"`
	AvgBayes       int16          `json:"avg_bayes"`
}

func (q *Queries) GetTopListLocations(ctx context.Context, arg GetTopListLocationsParams) ([]GetTopListLocationsRow, error) {
	regionType := ""

	if arg.RegionType.Valid {
		regionType = fmt.Sprintf("AND reg.id = %d", arg.RegionType.Int16)
	}

	//  https://fulmicoton.com/posts/bayesian_rating/
	getTopListQ := fmt.Sprintf(`SELECT 	
	row_number() over (ORDER BY %s DESC) as row_number,
		*
	FROM(
	SELECT
			*,
			(SELECT 5 * 4 + COALESCE(critic_score, 0) * COALESCE(critic_count, 0) / 5 + COALESCE(critic_count, 0)) as critic_bayes,
			(SELECT 50 + COALESCE(user_score, 0) * COALESCE(user_count, 0) / 50 + COALESCE(user_count, 0)) as user_bayes,
			((SELECT 50 + COALESCE(user_score, 0) * COALESCE(user_count, 0) / 50 + COALESCE(user_count, 0)) + (SELECT 5 * 4 + COALESCE(critic_score, 0) * COALESCE(critic_count, 0) / 5 + COALESCE(critic_count, 0)) ) / 2 as avg_bayes

	FROM (
			SELECT
					l.id,
					name,
					l.address,
					reg.region_name as region_name,
					l.google_maps_link,
					thumbnail,
					re.regency_name,
					(SELECT COALESCE(SUM(score), 0) from reviews re where re.is_from_critic = true and re.location_id = l.id) as critic_score,
					(SELECT COUNT(id) from reviews re where re.is_from_critic = true and re.location_id = l.id) as critic_count,
					(SELECT COALESCE(SUM(score), 0) from reviews re where re.is_from_critic = false and re.location_id = l.id) as user_score,
					(SELECT COUNT(id) from reviews re where re.is_from_critic = false and re.location_id = l.id) as user_count
			FROM locations l
			JOIN regencies re on re.id = l.regency_id
			JOIN provinces prov on prov.id = re.province_id
			JOIN regions reg on reg.id = prov.region_id
			WHERE approved_by IS NOT NULL %s ) iq1 ) iq2
	LIMIT $1
	OFFSET $2;`, arg.OrderBy, regionType)

	rows, err := q.db.QueryContext(ctx, getTopListQ, arg.Limit, arg.Offset)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	items := []GetTopListLocationsRow{}
	for rows.Next() {
		var i GetTopListLocationsRow
		if err := rows.Scan(
			&i.RowNumber,
			&i.ID,
			&i.Name,
			&i.Address,
			&i.RegionName,
			&i.GoogleMapsLink,
			&i.Thumbnail,
			&i.RegencyName,
			&i.CriticScore,
			&i.CriticCount,
			&i.UserScore,
			&i.UserCount,
			&i.CriticBayes,
			&i.UserBayes,
			&i.AvgBayes,
		); err != nil {
			return nil, err
		}
		i.TotalCount = i.UserCount + i.CriticCount
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}