package db

import (
	"context"
	"database/sql"
	"fmt"
	"slices"
	"strings"
	"time"

	"github.com/bluesky-social/indigo/atproto/syntax"
	"tangled.org/core/appview/models"
	"tangled.org/core/consts"
	"tangled.org/core/orm"
)

// RecentSpindles suggests spindles for the spindle picker: ones this user already
// pointed a repo at, most recently created first, plus the default spindle.
func RecentSpindles(ctx context.Context, e Execer, user syntax.DID) ([]string, error) {
	rows, err := e.QueryContext(ctx, `
		select spindle from repos
		where did = ? and coalesce(spindle, '') != ''
		group by spindle
		order by max(id) desc
		limit 5
	`, user)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var spindles []string
	for rows.Next() {
		var spindle string
		if err := rows.Scan(&spindle); err != nil {
			return nil, err
		}
		spindles = append(spindles, spindle)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}

	// a fresh account owns no repos, and a bare text input gives it nothing to go on
	if !slices.Contains(spindles, consts.DefaultSpindle) {
		spindles = append(spindles, consts.DefaultSpindle)
	}

	return spindles, nil
}

func GetSpindles(ctx context.Context, e Execer, filters ...orm.Filter) ([]models.Spindle, error) {
	var spindles []models.Spindle

	var conditions []string
	var args []any
	for _, filter := range filters {
		conditions = append(conditions, filter.Condition())
		args = append(args, filter.Arg()...)
	}

	whereClause := ""
	if conditions != nil {
		whereClause = " where " + strings.Join(conditions, " and ")
	}

	query := fmt.Sprintf(
		`select id, owner, instance, verified, created, needs_upgrade
		from spindles
		%s
		order by created
		`,
		whereClause,
	)

	rows, err := e.QueryContext(ctx, query, args...)

	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		var spindle models.Spindle
		var createdAt string
		var verified sql.NullString
		var needsUpgrade int

		if err := rows.Scan(
			&spindle.Id,
			&spindle.Owner,
			&spindle.Instance,
			&verified,
			&createdAt,
			&needsUpgrade,
		); err != nil {
			return nil, err
		}

		spindle.Created, err = time.Parse(time.RFC3339, createdAt)
		if err != nil {
			spindle.Created = time.Now()
		}

		if verified.Valid {
			t, err := time.Parse(time.RFC3339, verified.String)
			if err != nil {
				now := time.Now()
				spindle.Verified = &now
			}
			spindle.Verified = &t
		}

		if needsUpgrade != 0 {
			spindle.NeedsUpgrade = true
		}

		spindles = append(spindles, spindle)
	}

	return spindles, nil
}

func VerifySpindle(e Execer, filters ...orm.Filter) (int64, error) {
	var conditions []string
	var args []any
	for _, filter := range filters {
		conditions = append(conditions, filter.Condition())
		args = append(args, filter.Arg()...)
	}

	whereClause := ""
	if conditions != nil {
		whereClause = " where " + strings.Join(conditions, " and ")
	}

	query := fmt.Sprintf(`update spindles set verified = strftime('%%Y-%%m-%%dT%%H:%%M:%%SZ', 'now'), needs_upgrade = 0 %s`, whereClause)

	res, err := e.Exec(query, args...)
	if err != nil {
		return 0, err
	}

	return res.RowsAffected()
}
