package core

import (
	"strings"
	"time"

	"codeberg.org/shroff/phylum/server/internal/db"
	"github.com/google/uuid"
	"github.com/jackc/pgx/v5"
)

type Bookmark struct {
	ResourceID uuid.UUID
	Name       string
	Dir        bool
	Created    time.Time
}

func scanBookmark(row pgx.CollectableRow) (Bookmark, error) {
	var p Bookmark
	if err := row.Scan(
		&p.ResourceID,
		&p.Name,
		&p.Dir,
		&p.Created,
	); err != nil {
		return Bookmark{}, err
	}
	return p, nil
}

func AddBookmark(db db.TxHandler, u User, resource Resource, name string) (Bookmark, error) {
	if name == "" {
		name = resource.Name()
	}
	if CheckResourceNameInvalid(name) {
		return Bookmark{}, ErrResourceNameInvalid
	}

	const q = `INSERT INTO bookmarks(user_id, resource_id, name, dir)
VALUES ($1::INT, $2::UUID, $3::TEXT, $4::BOOLEAN)
ON CONFLICT(user_id, resource_id) DO UPDATE
SET
    created = CASE WHEN bookmarks.deleted IS NULL THEN CURRENT_TIMESTAMP ELSE bookmarks.created END,
    modified = CURRENT_TIMESTAMP,
    deleted = NULL,
    name = $3::TEXT
RETURNING resource_id, name, dir, created`

	if rows, err := db.Query(q, u.ID, resource.ID(), name, resource.Dir()); err != nil {
		return Bookmark{}, err
	} else if bookmark, err := pgx.CollectExactlyOneRow(rows, scanBookmark); err != nil {
		return Bookmark{}, err
	} else {
		return bookmark, nil
	}
}

func RemoveBookmark(db db.TxHandler, u User, id uuid.UUID) error {
	const q = "DELETE FROM bookmarks WHERE user_id = $1::INT AND resource_id = $2::UUID"
	_, err := db.Exec(q, u.ID, id)
	return err
}

func ListBookmarks(db db.Handler, u User, since int64) ([]Bookmark, error) {
	sb := strings.Builder{}
	sb.WriteString(`SELECT resource_id, name, dir, created
FROM bookmarks b
WHERE user_id = @user_id::INT
`)
	if since == 0 {
		sb.WriteString("AND deleted IS NULL\n")
	} else {
		sb.WriteString("AND modified >= @since::TIMESTAMP\n")
	}
	sb.WriteString("ORDER BY modified DESC")

	if rows, err := db.Query(sb.String(), pgx.NamedArgs{"user_id": u.ID, "since": time.UnixMilli(since).UTC()}); err != nil {
		return nil, err
	} else if bookmarks, err := pgx.CollectRows(rows, scanBookmark); err != nil {
		return nil, err
	} else {
		return bookmarks, nil
	}
}
