<?php

namespace GemBoards;

use cli\progress\Bar;
use GemBoards\Entities\Board;
use GemBoards\Entities\Card;
use GemBoards\Entities\Project;
use GemBoards\factories\FolderFactory;
use GemBoards\factories\UserFactory;
use GemBoards\Utils\Helper;
use GemBoardsPro\Entities\Sprint;
use WP_CLI;
use WP_CLI\ExitException;
use WP_CLI\NoOp;
use WP_User;
use function WP_CLI\Utils\make_progress_bar;

class GemboardsCLI {

	/**
	 * @var Bar|NoOp
	 */
	public static $progress;

	/**
	 * Generate dummy data
	 *
	 * ## OPTIONS
	 *
	 * [--projects=<count>]
	 * : Number of projects to generate
	 *
	 * [--boards=<count>]
	 * : Number of boards per project (legacy; ignored when --kanban-boards or --scrum-boards is set)
	 *
	 * [--board_type=<string>]
	 * : Board type for legacy --boards mode: kanban, scrum, or random (default)
	 *
	 * [--kanban-boards=<count>]
	 * : Number of Kanban boards to generate per project
	 *
	 * [--scrum-boards=<count>]
	 * : Number of Scrum boards to generate per project
	 *
	 * [--sprints=<count>]
	 * : Number of sprints per Scrum board (default: max(cards/4, 1))
	 *
	 * [--labels=<count>]
	 * : Number of gemboards_label taxonomy terms to seed before card creation
	 *
	 * [--cards=<count>]
	 * : Number of cards to generate per board
	 *
	 * [--users=<count>]
	 * : Number of users to generate and assign to projects
	 *
	 * @when after_wp_load
	 * @throws ExitException
	 */
	public function generate( $args, $assoc_args ) {
		$projects_count = absint( $assoc_args['projects'] ?? 1 );

		if ( ! isset( $assoc_args['cards'] ) ) {
			WP_CLI::error( __( 'Please specify cards count.', 'gemboards' ) );
		}
		$cards_count = absint( $assoc_args['cards'] );

		// Determine board mode: split (new) vs legacy
		$use_split = isset( $assoc_args['kanban-boards'] ) || isset( $assoc_args['scrum-boards'] );
		if ( $use_split && isset( $assoc_args['boards'] ) ) {
			WP_CLI::line( __( '--boards is ignored when --kanban-boards or --scrum-boards is set.', 'gemboards' ) );
		}

		if ( $use_split ) {
			$kanban_count = absint( $assoc_args['kanban-boards'] ?? 0 );
			$scrum_count  = absint( $assoc_args['scrum-boards'] ?? 0 );
			$boards_count = $kanban_count + $scrum_count;
		} else {
			$boards_count = absint( $assoc_args['boards'] ?? 1 );
			$board_type   = $assoc_args['board_type'] ?? null;
			$kanban_count = 0;
			$scrum_count  = 0;
		}

		$sprints_explicit = isset( $assoc_args['sprints'] ) ? absint( $assoc_args['sprints'] ) : null;
		$labels_count     = absint( $assoc_args['labels'] ?? 0 );

		// Pre-seed label taxonomy terms before any card creation
		if ( $labels_count > 0 ) {
			WP_CLI::line( sprintf(
				/* translators: %d number of labels being seeded */
				__( 'Seeding %d label(s)......', 'gemboards' ),
				$labels_count
			) );
			for ( $i = 1; $i <= $labels_count; $i++ ) {
				wp_insert_term( "Label $i", 'gemboards_label' );
			}
			WP_CLI::line( sprintf(
				/* translators: %d number of labels seeded */
				__( 'Seeded labels: (%d)', 'gemboards' ),
				$labels_count
			) );
			WP_CLI::line();
		}

		WP_CLI::line( __( 'Generating projects......', 'gemboards' ) );
		$projects = Project::factory()->create( $projects_count );
		if ( ! is_array( $projects ) ) {
			$projects = [ $projects ];
		}
		$project_ids = array_map( fn( $project ) => $project->get_id(), $projects );
		/* translators: %d is the count of generated projects */
		WP_CLI::line( sprintf( __( 'Generated projects: (%d)', 'gemboards' ), $projects_count ) );
		WP_CLI::line();

		$assignees   = [];
		$users_count = absint( $assoc_args['users'] ?? 0 );
		if ( $users_count ) {
			WP_CLI::line( __( 'Generating users......', 'gemboards' ) );
			$users = UserFactory::init()->with( [
				'projects' => $project_ids,
			] )->create( $users_count );
			if ( ! is_array( $users ) ) {
				$users = [ $users ];
			}
			foreach ( $users as $user ) {
				if ( $user instanceof WP_User ) {
					$assignees[] = $user->ID;
					update_user_meta( $user->ID, 'gemboards_seeded', 1 );
				}
			}
			/* translators: %d is the count of generated users */
			WP_CLI::line( sprintf( __( 'Generated users: (%d)', 'gemboards' ), $users_count ) );
			WP_CLI::line();
		}

		$GLOBALS['gemboards_generating'] = true;

		// Calculate the accurate progress bar total for each mode
		if ( $use_split ) {
			$sprints_per_board = null !== $sprints_explicit
				? $sprints_explicit
				: max( absint( $cards_count / 4 ), 1 );
			$total_items = $projects_count * (
				max( $boards_count - 1, 1 )                                                   // folder ticks
				+ $boards_count                                                                // board ticks
				+ ( Helper::has_pro() ? $scrum_count * $sprints_per_board : 0 )               // sprint ticks
				+ $boards_count * $cards_count                                                 // card ticks
			);
		} else {
			$total_items = ( $projects_count * $boards_count * $cards_count )
				+ ( $projects_count * max( $boards_count - 1, 1 ) )
				+ max( absint( ( $projects_count * $boards_count * $cards_count ) / 4 ), 1 );
		}

		self::$progress = make_progress_bar( 'Generating boards, cards, and more for each project', $total_items );

		foreach ( $projects as $project ) {
			$folder_count = max( $boards_count - 1, 1 );
			$folders      = FolderFactory::init()->with( [
				'project_id' => $project->get_id(),
			] )->create( $folder_count );
			if ( 1 === $folder_count ) {
				$folders = [ $folders ];
			}
			$folder_ids = array_map( fn( $folder ) => $folder->get_id(), $folders );
			$folder_ids = [ 0, ...$folder_ids ];

			if ( $use_split ) {
				// Build the typed board list
				$boards = [];
				foreach ( [ 'kanban' => $kanban_count, 'scrum' => $scrum_count ] as $type => $count ) {
					for ( $b = 0; $b < $count; $b++ ) {
						$board    = Board::factory()->with( [
							'project_id' => $project->get_id(),
							'folder_id'  => $folder_ids[ array_rand( $folder_ids ) ],
							'board_type' => $type,
						] )->create( 1 );
						$boards[] = $board;
					}
				}
			} else {
				/** @var Board[] $boards */
				$boards = Board::factory()->with( [
					'project_id' => $project->get_id(),
					'folder_id'  => $folder_ids[ array_rand( $folder_ids ) ],
					'board_type' => ( null === $board_type || 'random' === $board_type ) ? false : $board_type,
				] )->create( $boards_count );
				if ( 1 === $boards_count ) {
					$boards = [ $boards ];
				}
			}

			foreach ( $boards as $board ) {
				if ( ! $board->get_id() ) {
					continue;
				}

				$sprints = [];
				if ( 'scrum' === $board->get_board_type() && Helper::has_pro() ) {
					$sprint_count = null !== $sprints_explicit
						? $sprints_explicit
						: max( absint( $cards_count / 4 ), 1 );
					$created_sprints = Sprint::factory()->with( [
						'board_id' => $board->get_id(),
					] )->create( $sprint_count );
					if ( ! is_array( $created_sprints ) ) {
						$created_sprints = [ $created_sprints ];
					}
					$sprints = array_map( fn( $sprint ) => $sprint->get_id(), $created_sprints );
					$sprints = [ 0, ...$sprints ];
				}
				$column_ids = array_map( fn( $column ) => $column->get_id(), $board->get_columns() );

				for ( $i = 0; $i < $cards_count; $i++ ) {
					Card::factory()
						->with( [
							'board_id'  => $board->get_id(),
							'assignees' => $assignees,
							'sprint_id' => 'scrum' === $board->get_board_type() ? $sprints[ array_rand( $sprints ) ] : 0,
							'column_id' => $column_ids[ array_rand( $column_ids ) ],
						] )
						->create();
				}
			}
		}

		$GLOBALS['gemboards_generating'] = false;
		self::$progress->finish();
		WP_CLI::line();

		$total_boards = $projects_count * $boards_count;
		$total_cards  = $projects_count * $boards_count * $cards_count;
		WP_CLI::success( "Total Generated Items:\nProjects count: $projects_count\nBoards count: $total_boards\nCards count: $total_cards" );
	}

	/**
	 * Duplicate existing cards on a board for load-testing / seeding
	 *
	 * ## OPTIONS
	 *
	 * --board=<id>
	 * : ID of the board whose cards should be duplicated
	 *
	 * [--times=<count>]
	 * : How many additional copies of each card to create (default: 3)
	 *
	 * ## EXAMPLES
	 *
	 *     wp gemboards duplicate --board=5
	 *     wp gemboards duplicate --board=5 --times=10
	 *
	 * @when after_wp_load
	 * @throws ExitException
	 */
	public function duplicate( $args, $assoc_args ) {
		global $wpdb;

		if ( ! isset( $assoc_args['board'] ) ) {
			WP_CLI::error( __( 'Please specify a board ID via --board=<id>.', 'gemboards' ) );
		}

		$board_id  = absint( $assoc_args['board'] );
		$times     = absint( $assoc_args['times'] ?? 3 );
		$prefix    = $wpdb->prefix;
		$cards_tbl = $prefix . Helper::BOARD_CARD_TABLE_NAME;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		$board_exists = $wpdb->get_var( $wpdb->prepare(
			"SELECT id FROM `{$prefix}gemboards_boards` WHERE id = %d LIMIT 1",
			$board_id
		) );

		if ( ! $board_exists ) {
			WP_CLI::error( sprintf( __( 'No board found with ID %d.', 'gemboards' ), $board_id ) );
		}

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		$source_cards = $wpdb->get_results( $wpdb->prepare(
			"SELECT * FROM `$cards_tbl` WHERE board_id = %d AND archived = 0",
			$board_id
		) );

		if ( empty( $source_cards ) ) {
			WP_CLI::warning( __( 'No cards found on this board. Nothing to duplicate.', 'gemboards' ) );
			return;
		}

		$total    = count( $source_cards ) * $times;
		$progress = make_progress_bar( "Duplicating cards ($times× each)", $total );
		$created  = 0;

		for ( $t = 0; $t < $times; $t++ ) {
			foreach ( $source_cards as $card ) {
				$source_post = get_post( (int) $card->task_id );
				if ( ! $source_post ) {
					$progress->tick();
					continue;
				}

				$new_post_id = wp_insert_post( [
					'post_title'   => $source_post->post_title,
					'post_content' => $source_post->post_content,
					'post_type'    => Helper::BOARD_TASK_POST_TYPE,
					'post_status'  => 'publish',
					'post_author'  => $source_post->post_author,
				] );

				if ( is_wp_error( $new_post_id ) || ! $new_post_id ) {
					$progress->tick();
					continue;
				}

				foreach ( [ 'gemboards_priority', 'gemboards_label' ] as $taxonomy ) {
					$terms = wp_get_object_terms( (int) $card->task_id, $taxonomy, [ 'fields' => 'slugs' ] );
					if ( ! is_wp_error( $terms ) && ! empty( $terms ) ) {
						wp_set_object_terms( $new_post_id, $terms, $taxonomy );
					}
				}

				$wpdb->insert( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
					$cards_tbl,
					[
						'task_id'      => $new_post_id,
						'board_id'     => (int) $card->board_id,
						'column_id'    => (int) $card->column_id,
						'sprint_id'    => (int) $card->sprint_id,
						'sort_order'   => (int) $card->sort_order,
						'is_completed' => (int) $card->is_completed,
						'archived'     => (int) $card->archived,
						'due_date'     => $card->due_date,
						'story_point'  => (int) $card->story_point,
					],
					[ '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s', '%d' ]
				);

				$created++;
				$progress->tick();
			}
		}

		$progress->finish();
		WP_CLI::line();
		WP_CLI::success( sprintf(
			/* translators: 1: number of cards created, 2: board ID */
			__( 'Created %1$d card copies on board %2$d.', 'gemboards' ),
			$created,
			$board_id
		) );
	}

	/**
	 * Remove all GemBoards seeder-generated data
	 *
	 * ## OPTIONS
	 *
	 * [--yes]
	 * : Skip the confirmation prompt
	 *
	 * [--users]
	 * : Also delete WP users tagged with gemboards_seeded meta
	 *
	 * ## EXAMPLES
	 *
	 *     wp gemboards clean --yes
	 *     wp gemboards clean --yes --users
	 *
	 * @when after_wp_load
	 * @throws ExitException
	 */
	public function clean( $args, $assoc_args ) {
		global $wpdb;

		$skip_confirm = WP_CLI\Utils\get_flag_value( $assoc_args, 'yes', false );
		$clean_users  = isset( $assoc_args['users'] );

		$what = 'projects, boards, columns, cards, folders';
		if ( Helper::has_pro() ) {
			$what .= ', sprints';
		}
		if ( $clean_users ) {
			$what .= ', seeder users';
		}

		if ( ! $skip_confirm ) {
			WP_CLI::confirm( "This will DELETE all GemBoards data ($what). Continue?" );
		}

		// 1. Delete gemboards_task WP posts in batches to avoid memory spikes
		$posts_deleted = 0;
		$batch         = 200;
		do {
			$ids = get_posts( [
				'post_type'      => Helper::BOARD_TASK_POST_TYPE,
				'post_status'    => 'any',
				'posts_per_page' => $batch,
				'fields'         => 'ids',
			] );
			foreach ( $ids as $post_id ) {
				wp_delete_post( (int) $post_id, true );
				$posts_deleted++;
			}
		} while ( count( $ids ) === $batch );

		// 2. Truncate custom tables in FK dependency order
		$prefix = $wpdb->prefix;
		$tables = [
			$prefix . Helper::CARD_ASSIGNEES_TABLE_NAME,
			$prefix . Helper::BOARD_CARD_TABLE_NAME,
			$prefix . Helper::BOARD_COLUMN_TABLE_NAME,
			$prefix . Helper::BOARDS_TABLE_NAME,
			$prefix . Helper::FOLDERS_TABLE_NAME,
			$prefix . Helper::PROJECTS_TABLE_NAME,
			$prefix . Helper::ROLES_TABLE_NAME,
		];
		foreach ( $tables as $table ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared
			$wpdb->query( "TRUNCATE TABLE `$table`" );
		}

		// 3. Pro sprint table
		if ( Helper::has_pro() ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
			$wpdb->query( "TRUNCATE TABLE `{$prefix}gemboards_pro_sprints`" ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
			WP_CLI::line( __( 'Pro sprints table truncated.', 'gemboards' ) );
		} else {
			WP_CLI::line( __( 'Skipping sprint cleanup: GemBoards Pro is not active.', 'gemboards' ) );
		}

		// 4. Optionally delete seeder-tagged WP users
		$users_deleted = 0;
		if ( $clean_users ) {
			$seeded_ids = get_users( [
				'meta_key'   => 'gemboards_seeded',
				'meta_value' => '1',
				'fields'     => 'ID',
			] );
			foreach ( $seeded_ids as $uid ) {
				wp_delete_user( (int) $uid );
				$users_deleted++;
			}
		}

		// 5. Summary
		WP_CLI::line();
		WP_CLI::line( "Deleted: $posts_deleted cards (WP posts)" );
		WP_CLI::line( 'Deleted: all projects, boards, columns, folders, roles' );
		if ( $clean_users ) {
			WP_CLI::line( "Deleted: $users_deleted seeder users" );
		}
		WP_CLI::success( __( 'Database cleaned.', 'gemboards' ) );
	}
}
