package postgres

import (
	"database/sql"
	"fmt"
	"strings"

	"github.com/NyaaPantsu/nyaa/common"
	"github.com/NyaaPantsu/nyaa/model"
)

// build sql query from SearchParam for torrent search
func searchParamToTorrentQuery(param *common.TorrentParam) (q sqlQuery) {
	counter := 1
	q.query = fmt.Sprintf("SELECT %s FROM %s ", torrentSelectColumnsFull, tableTorrents)
	if param.Category.IsSet() {
		q.query += fmt.Sprintf("WHERE category = $%d AND sub_category = $%d ", counter, counter+1)
		q.params = append(q.params, param.Category.Main, param.Category.Sub)
		counter += 2
	}
	if counter > 1 {
		q.query += "AND "
	} else {
		q.query += "WHERE "
	}

	q.query += fmt.Sprintf("status >= $%d ", counter)
	q.params = append(q.params, param.Status)
	counter++
	if param.UserID > 0 {
		q.query += fmt.Sprintf("AND uploader = $%d ", counter)
		q.params = append(q.params, param.UserID)
		counter++
	}

	notnulls := strings.Split(param.NotNull, ",")
	for idx := range notnulls {
		k := strings.ToLower(strings.TrimSpace(notnulls[idx]))
		switch k {
		case "date":
		case "downloads":
		case "filesize":
		case "website_link":
		case "deleted_at":
		case "seeders":
		case "leechers":
		case "completed":
		case "last_scrape":
			q.query += fmt.Sprintf("AND %s IS NOT NULL ", k)
			break
		default:
			break
		}
	}

	nulls := strings.Split(param.Null, ",")
	for idx := range nulls {
		k := strings.ToLower(strings.TrimSpace(nulls[idx]))
		switch k {
		case "date":
		case "downloads":
		case "filesize":
		case "website_link":
		case "deleted_at":
		case "seeders":
		case "leechers":
		case "completed":
		case "last_scrape":
			q.query += fmt.Sprintf("AND %s IS NULL ", k)
			break
		default:
			break
		}
	}

	nameLikes := strings.Split(param.NameLike, ",")
	for idx := range nameLikes {
		q.query += fmt.Sprintf("AND torrent_name ILIKE $%d", counter)
		q.params = append(q.params, strings.TrimSpace(nameLikes[idx]))
		counter++
	}

	var sort string
	switch param.Sort {
	case common.Name:
		sort = "torrent_name"
		break
	case common.Date:
		sort = "date"
		break
	case common.Downloads:
		sort = "downloads"
		break
	case common.Size:
		sort = "filesize"
		break
	case common.Seeders:
		sort = "seeders"
		break
	case common.Leechers:
		sort = "leechers"
		break
	case common.Completed:
		sort = "completed"
		break
	case common.ID:
	default:
		sort = "torrent_id"
	}

	q.query += fmt.Sprintf("ORDER BY %s ", sort)
	if param.Order {
		q.query += "ASC "
	} else {
		q.query += "DESC "
	}

	if param.Max > 0 {
		q.query += fmt.Sprintf("LIMIT $%d ", counter)
		q.params = append(q.params, param.Max)
		counter++
	}
	if param.Offset > 0 {
		q.query += fmt.Sprintf("OFFSET $%d ", counter)
		q.params = append(q.params, param.Offset)
		counter++
	}
	return
}

func (db *Database) GetTorrentsWhere(param *common.TorrentParam) (torrents []model.Torrent, err error) {
	if param.TorrentID > 0 {
		var torrent model.Torrent
		var has bool
		torrent, has, err = db.GetTorrentByID(param.TorrentID)
		if has {
			torrents = append(torrents, torrent)
		}
		return
	}

	if param.All {
		torrents, err = db.GetAllTorrents(param.Offset, param.Max)
		return
	}

	q := searchParamToTorrentQuery(param)
	err = q.Query(db.conn, func(rows *sql.Rows) error {
		if param.Max == 0 {
			torrents = make([]model.Torrent, 0, 128)
		} else {
			torrents = make([]model.Torrent, 0, param.Max)
		}

		for rows.Next() {
			var t model.Torrent
			scanTorrentColumnsFull(rows, &t)
			torrents = append(torrents, t)
			// grow as needed
			if len(torrents) >= cap(torrents) {
				newtorrents := make([]model.Torrent, cap(torrents), cap(torrents)*3/2) // XXX: adjust as needed
				copy(newtorrents, torrents)
				torrents = newtorrents
			}
		}
		return nil
	})
	return
}
