package migrations

import (
	"context"
	"fmt"
	"time"

	"git.handmade.network/hmn/hmn/src/migration/types"
	"git.handmade.network/hmn/hmn/src/oops"
	"github.com/jackc/pgx/v5"
)

func init() {
	registerMigration(DeleteInactiveUsers{})
}

type DeleteInactiveUsers struct{}

func (m DeleteInactiveUsers) Version() types.MigrationVersion {
	return types.MigrationVersion(time.Date(2021, 8, 8, 13, 46, 55, 0, time.UTC))
}

func (m DeleteInactiveUsers) Name() string {
	return "DeleteInactiveUsers"
}

func (m DeleteInactiveUsers) Description() string {
	return "Delete inactive users and expired onetimetokens"
}

func (m DeleteInactiveUsers) Up(ctx context.Context, tx pgx.Tx) error {
	var err error
	res, err := tx.Exec(ctx,
		`
		DELETE FROM handmade_passwordresetrequest
		USING handmade_onetimetoken
		WHERE handmade_onetimetoken.expires < $1 AND handmade_passwordresetrequest.confirmation_token_id = handmade_onetimetoken.id;
		`,
		time.Now(),
	)
	if err != nil {
		return oops.New(err, "failed to delete password reset requests")
	}
	fmt.Printf("Deleted %v expired password reset requests.\n", res.RowsAffected())

	fmt.Printf("Deleting inactive users. This might take a minute.\n")
	res, err = tx.Exec(ctx,
		`
		DELETE FROM auth_user
		WHERE status = 1 AND date_joined < $1;
		`,
		time.Now().Add(-(time.Hour * 24 * 7)),
	)
	if err != nil {
		return oops.New(err, "failed to delete inactive users")
	}
	fmt.Printf("Deleted %v inactive users.\n", res.RowsAffected())

	_, err = tx.Exec(ctx, `
		ALTER TABLE handmade_onetimetoken
			DROP used;
		ALTER TABLE handmade_onetimetoken
			ADD owner_id INT REFERENCES auth_user(id) ON DELETE CASCADE;

		ALTER TABLE handmade_userpending
			DROP CONSTRAINT handma_activation_token_id_0b4a4b06_fk_handmade_onetimetoken_id;
	`)

	_, err = tx.Exec(ctx, `
		UPDATE handmade_userpending
			SET activation_token_id = NULL
			WHERE (SELECT count(*) AS ct FROM handmade_onetimetoken WHERE id = activation_token_id) = 0;
	`)

	res, err = tx.Exec(ctx,
		`
		DELETE FROM handmade_onetimetoken
		WHERE expires < $1
		`,
		time.Now(),
	)
	if err != nil {
		return oops.New(err, "failed to delete expired tokens")
	}
	fmt.Printf("Deleted %v expired tokens.\n", res.RowsAffected())

	fmt.Printf("Setting owner_id on onetimetoken\n")
	_, err = tx.Exec(ctx, `
		UPDATE handmade_onetimetoken
			SET owner_id = (SELECT id FROM auth_user WHERE username = (SELECT username FROM handmade_userpending WHERE activation_token_id = handmade_onetimetoken.id LIMIT 1))
			WHERE token_type = 1;
	`)
	if err != nil {
		return oops.New(err, "failed to set owner_id on onetimetoken")
	}
	_, err = tx.Exec(ctx, `
		UPDATE handmade_onetimetoken
			SET owner_id = (SELECT user_id FROM handmade_passwordresetrequest WHERE confirmation_token_id = handmade_onetimetoken.id)
			WHERE token_type = 2;
	`)
	if err != nil {
		return oops.New(err, "failed to set owner_id on onetimetoken")
	}

	fmt.Printf("Setting registration_ip on auth_user\n")
	_, err = tx.Exec(ctx, `
		UPDATE auth_user
			SET registration_ip = (SELECT ip FROM handmade_userpending WHERE handmade_userpending.username = auth_user.username LIMIT 1);
	`)
	if err != nil {
		return oops.New(err, "failed to set owner_id on onetimetoken")
	}

	return nil
}

func (m DeleteInactiveUsers) Down(ctx context.Context, tx pgx.Tx) error {
	panic("Implement me")
}
