package migrations

import (
	"context"
	"time"

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

/*
Phase 1. Migrate the schemas for all the tables that will stick around through this
whole process.
*/

func init() {
	registerMigration(RemoveMemberAndExtended{})
}

type RemoveMemberAndExtended struct{}

func (m RemoveMemberAndExtended) Version() types.MigrationVersion {
	return types.MigrationVersion(time.Date(2021, 4, 11, 19, 57, 47, 0, time.UTC))
}

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

func (m RemoveMemberAndExtended) Description() string {
	return "Remove the member and member extended records, collapsing their data into users"
}

func (m RemoveMemberAndExtended) Up(ctx context.Context, tx pgx.Tx) error {
	// Creates a column that will eventually be a foreign key to auth_user.
	createUserColumn := func(ctx context.Context, tx pgx.Tx, table string, before string, notNull bool) {
		nullConstraint := ""
		if notNull {
			nullConstraint = "NOT NULL"
		}

		_, err := tx.Exec(ctx, `
			ALTER TABLE `+table+`
				ADD plz_rename INT `+nullConstraint+` DEFAULT 99999;
			UPDATE `+table+` SET plz_rename = `+before+`;
		`)
		if err != nil {
			panic(oops.New(err, "failed to update table %s to point at users instead of members", table))
		}
	}

	// Migrate a lot of simple foreign keys
	createUserColumn(ctx, tx, "handmade_communicationchoice", "member_id", true)
	createUserColumn(ctx, tx, "handmade_communicationsubcategory", "member_id", true)
	createUserColumn(ctx, tx, "handmade_communicationsubthread", "member_id", true)
	createUserColumn(ctx, tx, "handmade_discord", "member_id", true)
	createUserColumn(ctx, tx, "handmade_categorylastreadinfo", "member_id", true)
	createUserColumn(ctx, tx, "handmade_threadlastreadinfo", "member_id", true)
	createUserColumn(ctx, tx, "handmade_posttextversion", "editor_id", false)
	createUserColumn(ctx, tx, "handmade_post", "author_id", false)
	createUserColumn(ctx, tx, "handmade_member_projects", "member_id", true)

	// Directly associate links with members
	_, err := tx.Exec(ctx, `
		ALTER TABLE handmade_links
			ADD COLUMN user_id INTEGER DEFAULT 99999,
			ADD COLUMN project_id INTEGER DEFAULT 99999;
		
		UPDATE handmade_links
		SET (user_id) = (
			SELECT mem.user_id
			FROM
				handmade_memberextended_links AS mlinks
				JOIN handmade_memberextended AS memext ON memext.id = mlinks.memberextended_id
				JOIN handmade_member AS mem ON mem.extended_id = memext.id
			WHERE
				mlinks.links_id = handmade_links.id
		);

		UPDATE handmade_links
		SET (project_id) = (
			SELECT proj.id
			FROM
				handmade_project_links AS plinks
				JOIN handmade_project AS proj ON proj.id = plinks.project_id
			WHERE
				plinks.links_id = handmade_links.id
		);
	`)
	if err != nil {
		return oops.New(err, "failed to associate links with members and projects")
	}

	_, err = tx.Exec(ctx, `
		ALTER TABLE auth_user
			-- From handmade_member --
			ADD blurb VARCHAR(140) NOT NULL DEFAULT '',
			ADD name VARCHAR(255) NOT NULL DEFAULT '',
			ADD signature TEXT NOT NULL DEFAULT '',
			ADD avatar VARCHAR(100),
			ADD location VARCHAR(100) NOT NULL DEFAULT '',
			-- ordering is dropped
			-- posts is dropped
			-- profileviews is dropped
			-- thanked is dropped
			ADD timezone VARCHAR(255),
			ADD color_1 VARCHAR(6),
			ADD color_2 VARCHAR(6),
			ADD darktheme BOOLEAN NOT NULL DEFAULT FALSE,
			-- extended_id is dropped
			-- project_count_all is dropped
			-- project_count_public is dropped
			-- matrix_username is dropped
			-- set_matrix_display_name is dropped
			ADD edit_library BOOLEAN NOT NULL DEFAULT FALSE,
			ADD discord_delete_snippet_on_message_delete BOOLEAN NOT NULL DEFAULT TRUE,
			ADD discord_save_showcase BOOLEAN NOT NULL DEFAULT TRUE,

			-- From handmade_memberextended --
			ADD bio TEXT NOT NULL DEFAULT '',
			ADD showemail BOOLEAN NOT NULL DEFAULT FALSE;
			-- sendemail is dropped
			-- joomlaid is dropped
			-- lastResetTime is dropped
			-- resetCount is dropped
			-- requireReset is dropped
			-- birthdate is dropped
		
		UPDATE auth_user
		SET (
			blurb,
			name,
			signature,
			avatar,
			location,
			timezone,
			color_1,
			color_2,
			darktheme,
			edit_library,
			discord_delete_snippet_on_message_delete,
			discord_save_showcase,
			bio,
			showemail
		) = (
			SELECT
				COALESCE(blurb, ''),
				COALESCE(name, ''),
				COALESCE(signature, ''),
				avatar,
				COALESCE(location, ''),
				timezone,
				color_1,
				color_2,
				darktheme,
				edit_library,
				discord_delete_snippet_on_message_delete,
				discord_save_showcase,
				COALESCE(bio, ''),
				showemail
			FROM
				handmade_member AS mem
				JOIN handmade_memberextended AS mext ON mem.extended_id = mext.id
			WHERE
				mem.user_id = auth_user.id
		);
		
		ALTER TABLE auth_user
			ALTER timezone SET NOT NULL,
			ALTER color_1 SET NOT NULL,
			ALTER color_2 SET NOT NULL;
	`)
	if err != nil {
		return oops.New(err, "failed to copy fields to auth_user")
	}

	return nil
}

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