package tag

import (
	"context"
	"database/sql"
	"strings"
	"time"
)

type Row struct {
	ID        int64
	Name      string
	CreatedAt time.Time
}

type ItemTag struct {
	ItemID int64
	TagID  int64
	Name   string
}

func QList(ctx context.Context, db *sql.DB) ([]Row, error) {
	rows, err := db.QueryContext(ctx, `
		SELECT id, name, created_at
		FROM tag
		ORDER BY name ASC
	`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var tags []Row
	for rows.Next() {
		var row Row
		if err := rows.Scan(&row.ID, &row.Name, &row.CreatedAt); err != nil {
			return nil, err
		}
		tags = append(tags, row)
	}
	return tags, rows.Err()
}

func QListByItem(ctx context.Context, db *sql.DB, itemID int64) ([]Row, error) {
	rows, err := db.QueryContext(ctx, `
		SELECT t.id, t.name, t.created_at
		FROM tag t
		JOIN item_tag it ON it.tag_id = t.id
		WHERE it.item_id = $1
		ORDER BY t.name ASC
	`, itemID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var tags []Row
	for rows.Next() {
		var row Row
		if err := rows.Scan(&row.ID, &row.Name, &row.CreatedAt); err != nil {
			return nil, err
		}
		tags = append(tags, row)
	}
	return tags, rows.Err()
}

func QListItemTags(ctx context.Context, db *sql.DB) ([]ItemTag, error) {
	rows, err := db.QueryContext(ctx, `
		SELECT it.item_id, t.id, t.name
		FROM item_tag it
		JOIN tag t ON t.id = it.tag_id
		ORDER BY t.name ASC
	`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var entries []ItemTag
	for rows.Next() {
		var row ItemTag
		if err := rows.Scan(&row.ItemID, &row.TagID, &row.Name); err != nil {
			return nil, err
		}
		entries = append(entries, row)
	}
	return entries, rows.Err()
}

func QUpsert(ctx context.Context, db *sql.DB, name string) (Row, error) {
	cleaned := strings.TrimSpace(strings.ToLower(name))
	var row Row
	err := db.QueryRowContext(ctx, `
		INSERT INTO tag (name)
		VALUES ($1)
		ON CONFLICT (name) DO UPDATE SET name = EXCLUDED.name
		RETURNING id, name, created_at
	`, cleaned).Scan(&row.ID, &row.Name, &row.CreatedAt)
	return row, err
}

func QReplaceItemTags(ctx context.Context, db *sql.DB, itemID int64, names []string) error {
	tx, err := db.BeginTx(ctx, nil)
	if err != nil {
		return err
	}
	defer tx.Rollback()

	_, err = tx.ExecContext(ctx, `DELETE FROM item_tag WHERE item_id = $1`, itemID)
	if err != nil {
		return err
	}

	for _, name := range names {
		if strings.TrimSpace(name) == "" {
			continue
		}
		var tagID int64
		err = tx.QueryRowContext(ctx, `
			INSERT INTO tag (name)
			VALUES ($1)
			ON CONFLICT (name) DO UPDATE SET name = EXCLUDED.name
			RETURNING id
		`, strings.TrimSpace(strings.ToLower(name))).Scan(&tagID)
		if err != nil {
			return err
		}

		_, err = tx.ExecContext(ctx, `
			INSERT INTO item_tag (item_id, tag_id)
			VALUES ($1, $2)
			ON CONFLICT DO NOTHING
		`, itemID, tagID)
		if err != nil {
			return err
		}
	}

	return tx.Commit()
}
