package cleandb

import (
	"context"
	"fmt"
	"time"

	dbpkg "codeberg.org/u1f320/pronouns.cc/backend/db"
	"github.com/georgysavva/scany/v2/pgxscan"
	"github.com/joho/godotenv"
	"github.com/rs/xid"
	"github.com/urfave/cli/v2"
)

var Command = &cli.Command{
	Name:   "clean",
	Usage:  "Clean deleted tokens + users daily",
	Action: run,
}

func run(c *cli.Context) error {
	err := godotenv.Load()
	if err != nil {
		fmt.Println("error loading .env file:", err)
		return err
	}

	ctx := context.Background()

	db, err := dbpkg.New()
	if err != nil {
		fmt.Println("error opening database:", err)
		return err
	}
	defer db.Close()

	fmt.Println("opened database")

	fmt.Println("deleting invalidated tokens")

	ct, err := db.Exec(ctx, "DELETE FROM tokens WHERE invalidated = true OR expires < $1", time.Now())
	if err != nil {
		fmt.Println("executing query:", err)
		return err
	}

	fmt.Printf("deleted %v invalidated or expired tokens\n", ct.RowsAffected())

	fmt.Println("deleting expired export files")
	var exports []dbpkg.DataExport
	err = pgxscan.Select(ctx, db, &exports, "SELECT * FROM data_exports WHERE created_at < $1", time.Now().Add(-dbpkg.KeepExportTime))
	if err != nil {
		fmt.Println("error getting to-be-deleted export files:", err)
		return err
	}

	for _, de := range exports {
		err = db.DeleteExport(ctx, de)
		if err != nil {
			fmt.Printf("error deleting export %v: %v\n", de.ID, err)
			continue
		}

		fmt.Println("deleted export", de.ID)
	}

	fmt.Printf("deleted %v expired exports\n", len(exports))

	var users []dbpkg.User
	err = pgxscan.Select(ctx, db, &users, `SELECT * FROM users WHERE
	deleted_at IS NOT NULL AND
	(self_delete = true AND deleted_at < $1)
	OR (self_delete = false AND deleted_at < $2)
	ORDER BY id`, time.Now().Add(-dbpkg.SelfDeleteAfter), time.Now().Add(-dbpkg.ModDeleteAfter))
	if err != nil {
		fmt.Println("error getting to-be-deleted users:", err)
		return err
	}

	if len(users) == 0 {
		fmt.Println("there are no users pending deletion")
		return nil
	}

	for _, u := range users {
		members, err := db.UserMembers(ctx, u.ID, true)
		if err != nil {
			fmt.Printf("error getting members for user %v: %v\n", u.ID, err)
			continue
		}

		for _, m := range members {
			if m.Avatar == nil {
				continue
			}

			fmt.Printf("deleting avatars for member %v\n", m.ID)

			err = db.DeleteMemberAvatar(ctx, m.ID, *m.Avatar)
			if err != nil {
				fmt.Printf("error deleting avatars for member %v: %v", m.ID, err)
				continue
			}

			fmt.Printf("deleted avatars for member %v\n", m.ID)
		}

		if u.Avatar == nil {
			continue
		}

		fmt.Printf("deleting avatars for user %v\n", u.ID)

		err = db.DeleteUserAvatar(ctx, u.ID, *u.Avatar)
		if err != nil {
			fmt.Printf("error deleting avatars for user %v: %v", u.ID, err)
			continue
		}

		fmt.Printf("deleted avatars for user %v\n", u.ID)
	}

	ids := make([]xid.ID, len(users))
	for _, u := range users {
		ids = append(ids, u.ID)
	}

	ct, err = db.Exec(ctx, "DELETE FROM users WHERE id = ANY($1)", ids)
	if err != nil {
		fmt.Printf("error deleting users: %v\n", err)
		return err
	}

	fmt.Printf("deleted %v users!\n", ct.RowsAffected())
	return nil
}