<?php
/**
 * @license GPL-2.0-or-later
 * @link    https://wordpress.org/plugins/pressio-crm
 */
namespace PressioCRM\API;

defined( 'ABSPATH' ) || exit;

use PressioCRM\Framework\Controller;
use PressioCRM\Models\ContactModel;
use PressioCRM\Models\DealModel;
use PressioCRM\Models\TaskModel;
use PressioCRM\Models\TagModel;
use PressioCRM\Models\ActivityModel;
use PressioCRM\Models\PipelineModel;
use PressioCRM\Models\PipelineStageModel;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

class RestOnboarding extends Controller {

	protected $rest_base = 'onboarding';

	/** Option key that stores the IDs of all seeded records. */
	private const SAMPLE_IDS_OPTION = 'pressio_crm_sample_data_ids';

	public function register_routes(): void {
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/sample-data',
			[
				[
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => [ $this, 'seed_sample_data' ],
					'permission_callback' => [ $this, 'require_settings_cap' ],
					'args'                => [],
				],
				[
					'methods'             => WP_REST_Server::DELETABLE,
					'callback'            => [ $this, 'delete_sample_data' ],
					'permission_callback' => [ $this, 'require_settings_cap' ],
					'args'                => [],
				],
			]
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/complete',
			[
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'mark_complete' ],
				'permission_callback' => [ $this, 'require_settings_cap' ],
				'args'                => [],
			]
		);
	}

	/**
	 * Seed realistic sample data so new users can explore the CRM immediately.
	 * Stores all created IDs so they can be cleanly removed later.
	 */
	/** @return WP_REST_Response|WP_Error */
	public function seed_sample_data( WP_REST_Request $request ) {
		if ( get_option( 'pressio_crm_sample_data_loaded' ) ) {
			return $this->ok( [ 'message' => __( 'Sample data already loaded.', 'pressio-crm' ) ] );
		}

		// Lock early to prevent duplicate seeding from concurrent requests.
		update_option( 'pressio_crm_sample_data_loaded', true, false );

		$pipeline = PipelineModel::get_default();
		if ( ! $pipeline ) {
			delete_option( 'pressio_crm_sample_data_loaded' );
			return $this->bad_request(
				'pressio_crm_no_pipeline',
				__( 'No default pipeline found. Please complete pipeline setup first.', 'pressio-crm' )
			);
		}

		$stages = PipelineStageModel::get_for_pipeline( (int) $pipeline->id );
		if ( empty( $stages ) ) {
			delete_option( 'pressio_crm_sample_data_loaded' );
			return $this->bad_request(
				'pressio_crm_no_stages',
				__( 'Pipeline has no stages.', 'pressio-crm' )
			);
		}

		$owner_id    = get_current_user_id();
		$settings    = get_option( 'pressio_crm_settings', [] );
		$settings    = is_array( $settings ) ? $settings : [];
		$currency    = sanitize_text_field( $settings['currency'] ?? 'USD' );
		$pipeline_id = (int) $pipeline->id;

		// Track all created IDs for clean removal.
		$created_ids = [
			'contacts' => [],
			'deals'    => [],
			'tasks'    => [],
			'tags'     => [],
		];

		// ── Tags ──────────────────────────────────────────────────────────────
		$tags = $this->seed_tags( $created_ids );

		// ── Contacts ──────────────────────────────────────────────────────────
		$contacts = $this->seed_contacts( $owner_id, $tags, $created_ids );

		// ── Deals ─────────────────────────────────────────────────────────────
		$this->seed_deals( $contacts, $stages, $pipeline_id, $currency, $owner_id, $created_ids );

		// ── Tasks ─────────────────────────────────────────────────────────────
		$this->seed_tasks( $contacts, $owner_id, $created_ids );

		// Store the IDs so we can delete them later.
		update_option( self::SAMPLE_IDS_OPTION, $created_ids, false );

		return $this->created( [
			'message'  => __( 'Sample data loaded successfully.', 'pressio-crm' ),
			'contacts' => count( $created_ids['contacts'] ),
			'deals'    => count( $created_ids['deals'] ),
			'tasks'    => count( $created_ids['tasks'] ),
			'tags'     => count( $created_ids['tags'] ),
		] );
	}

	/**
	 * Delete all sample data that was created during onboarding.
	 *
	 * Strategy A — meta key: find contacts tagged _is_sample_data (primary, version-agnostic).
	 * Strategy B — seed emails: fallback for seeds before meta tagging was added.
	 * Strategy C — stored IDs option: last-resort fallback.
	 *
	 * Direct $wpdb DELETEs bypass soft-delete so rows are removed regardless of
	 * their deleted_at state. Always returns 200; reports how many rows were removed.
	 */
	/** @return WP_REST_Response|WP_Error */
	public function delete_sample_data( WP_REST_Request $request ) {
		global $wpdb;

		$contacts_table = $wpdb->prefix . 'pcrm_contacts';
		$meta_table     = $wpdb->prefix . 'pcrm_contact_meta';

		// Strategy A: contacts tagged with _is_sample_data meta (primary).
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$by_meta = $wpdb->get_col(
			$wpdb->prepare(
				'SELECT DISTINCT contact_id FROM %i WHERE meta_key = %s',
				$meta_table,
				'_is_sample_data'
			)
		);

		// Strategy B: known seed emails (covers seeds run before meta tagging).
		$seed_emails = [
			'sarah.mitchell@bloomflorist.com',
			'james.harrington@harringtonsons.com',
			'emily.chen@chensbakery.com',
			'michael.torres@torresconstruction.com',
			'rachel.green@greenfieldagency.com',
			'david.kim@kimtechsolutions.com',
			'sophie.williams@williamswellness.com',
			'lucas.bennett@bennettpropertygroup.com',
			'olivia.johnson@johnsonconsulting.com',
			'noah.carter@cartercafe.com',
			'ava.martinez@martexgroup.com',
			'ethan.brooks@brooksarchitecture.com',
			'isabella.cooper@cooperevents.com',
			'mason.reed@reedfinancial.com',
			'mia.thompson@thompsonhealthcare.com',
			'henry.davis@davisphotography.com',
			'charlotte.wilson@wilsonlawgroup.com',
			'alexander.moore@moorelogistics.com',
			'amelia.taylor@taylorandco.com',
			'william.anderson@andersonplumbing.com',
		];
		$email_placeholders = implode( ',', array_fill( 0, count( $seed_emails ), '%s' ) );
		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber -- $email_placeholders is a safe, dynamically-generated list of %s placeholders; count matches splat args.
		$by_email = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT id FROM %i WHERE email IN ({$email_placeholders})",
				$contacts_table,
				...$seed_emails
			)
		);
		// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber

		// Strategy C: IDs option stored at seed time.
		$stored          = get_option( self::SAMPLE_IDS_OPTION );
		$stored          = is_array( $stored ) ? $stored : [];
		$stored_contacts = array_map( 'absint', $stored['contacts'] ?? [] );
		$stored_tags     = array_map( 'absint', $stored['tags'] ?? [] );

		$contact_ids = array_values( array_unique( array_map( 'absint', array_merge( $by_meta, $by_email, $stored_contacts ) ) ) );

		$deleted = [ 'contacts' => 0, 'deals' => 0, 'tasks' => 0, 'tags' => 0 ];

		if ( ! empty( $contact_ids ) ) {
			$id_placeholders = implode( ',', array_fill( 0, count( $contact_ids ), '%d' ) );

			// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber -- $id_placeholders is a safe, dynamically-generated list of %d placeholders; count matches splat args.

			// Tasks.
			$tasks_table = $wpdb->prefix . 'pcrm_tasks';
			$deleted['tasks'] = (int) $wpdb->query(
				$wpdb->prepare(
					"DELETE FROM %i WHERE contact_id IN ({$id_placeholders})",
					$tasks_table,
					...$contact_ids
				)
			);

			// Deals.
			$deals_table = $wpdb->prefix . 'pcrm_deals';
			$deleted['deals'] = (int) $wpdb->query(
				$wpdb->prepare(
					"DELETE FROM %i WHERE contact_id IN ({$id_placeholders})",
					$deals_table,
					...$contact_ids
				)
			);

			// Activities.
			$activities_table = $wpdb->prefix . 'pcrm_activities';
			$wpdb->query(
				$wpdb->prepare(
					"DELETE FROM %i WHERE contact_id IN ({$id_placeholders})",
					$activities_table,
					...$contact_ids
				)
			);

			// Contact meta.
			$meta_table = $wpdb->prefix . 'pcrm_contact_meta';
			$wpdb->query(
				$wpdb->prepare(
					"DELETE FROM %i WHERE contact_id IN ({$id_placeholders})",
					$meta_table,
					...$contact_ids
				)
			);

			// Contact–tag pivot.
			$pivot_table = $wpdb->prefix . 'pcrm_contact_tag';
			$wpdb->query(
				$wpdb->prepare(
					"DELETE FROM %i WHERE contact_id IN ({$id_placeholders})",
					$pivot_table,
					...$contact_ids
				)
			);

			// Contacts.
			$deleted['contacts'] = (int) $wpdb->query(
				$wpdb->prepare(
					"DELETE FROM %i WHERE id IN ({$id_placeholders})",
					$contacts_table,
					...$contact_ids
				)
			);
			// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
		}

		// Tags (only delete seed tags — don't touch user tags).
		foreach ( $stored_tags as $tag_id ) {
			$model = TagModel::find( $tag_id );
			if ( $model ) {
				$model->force_delete();
				++$deleted['tags'];
			}
		}

		delete_option( self::SAMPLE_IDS_OPTION );
		delete_option( 'pressio_crm_sample_data_loaded' );

		$total_deleted = array_sum( $deleted );

		if ( 0 === $total_deleted ) {
			return $this->ok( [
				'message' => __( 'No seeded sample contacts were found. Sample data may have already been removed, or was never loaded during onboarding.', 'pressio-crm' ),
				'deleted' => $deleted,
			] );
		}

		return $this->ok( [
			'message' => sprintf(
				/* translators: 1: contacts, 2: deals, 3: tasks */
				__( 'Sample data removed: %1$d contact(s), %2$d deal(s), %3$d task(s).', 'pressio-crm' ),
				$deleted['contacts'],
				$deleted['deals'],
				$deleted['tasks']
			),
			'deleted' => $deleted,
		] );
	}

	public function mark_complete( WP_REST_Request $request ): WP_REST_Response {
		update_option( 'pressio_crm_onboarding_done', true, false );
		delete_option( 'pressio_crm_show_onboarding' );

		return $this->ok( [ 'done' => true ] );
	}

	private function seed_tags( array &$created_ids ): array {
		$tag_data = [
			[ 'name' => 'VIP',      'color' => '#f59e0b' ],
			[ 'name' => 'Hot Lead', 'color' => '#ef4444' ],
			[ 'name' => 'Retainer', 'color' => '#10b981' ],
		];

		$tags = [];
		foreach ( $tag_data as $t ) {
			$existing = TagModel::find_by_slug( sanitize_title( $t['name'] ) );
			if ( $existing ) {
				$tags[] = $existing;
				// Don't track pre-existing tags — we won't delete them.
			} else {
				$tag    = TagModel::create( [
					'name'  => $t['name'],
					'slug'  => sanitize_title( $t['name'] ),
					'color' => $t['color'],
				] );
				$tags[] = $tag;
				$created_ids['tags'][] = (int) $tag->id;
			}
		}

		return $tags;
	}

	private function seed_contacts( int $owner_id, array $tags, array &$created_ids ): array {
		// 20 contacts sourced from demo-contacts.csv — realistic small-business CRM data.
		// tags: 0=VIP, 1=Hot Lead, 2=Retainer
		$contact_data = [
			[
				'first_name' => 'Sarah',
				'last_name'  => 'Mitchell',
				'email'      => 'sarah.mitchell@bloomflorist.com',
				'phone'      => '+1 (312) 555-0182',
				'company'    => 'Bloom Florist',
				'job_title'  => 'Owner',
				'city'       => 'Chicago',
				'state'      => 'IL',
				'country'    => 'US',
				'status'     => 'customer',
				'source'     => 'referral',
				'notes'      => 'Long-term retainer client. Handles social herself — just needs monthly SEO and reporting.',
				'tags'       => [ 0, 2 ],
			],
			[
				'first_name' => 'James',
				'last_name'  => 'Harrington',
				'email'      => 'james.harrington@harringtonsons.com',
				'phone'      => '+1 (617) 555-0247',
				'company'    => 'Harrington & Sons',
				'job_title'  => 'Marketing Director',
				'city'       => 'Boston',
				'state'      => 'MA',
				'country'    => 'US',
				'status'     => 'lead',
				'source'     => 'website',
				'notes'      => 'Interested in SEO retainer. Asked for case studies from similar B2B clients.',
				'tags'       => [ 1 ],
			],
			[
				'first_name' => 'Emily',
				'last_name'  => 'Chen',
				'email'      => 'emily.chen@chensbakery.com',
				'phone'      => '+1 (415) 555-0391',
				'company'    => "Chen's Bakery",
				'job_title'  => 'Owner',
				'city'       => 'San Francisco',
				'state'      => 'CA',
				'country'    => 'US',
				'status'     => 'customer',
				'source'     => 'referral',
				'notes'      => 'Website + Google Ads client since 2023. Very responsive and easy to work with.',
				'tags'       => [ 0, 2 ],
			],
			[
				'first_name' => 'Michael',
				'last_name'  => 'Torres',
				'email'      => 'michael.torres@torresconstruction.com',
				'phone'      => '+1 (602) 555-0158',
				'company'    => 'Torres Construction',
				'job_title'  => 'CEO',
				'city'       => 'Phoenix',
				'state'      => 'AZ',
				'country'    => 'US',
				'status'     => 'lead',
				'source'     => 'contact_form',
				'notes'      => 'Wants full site redesign. Currently on Squarespace. Submitted form after seeing our portfolio on Behance.',
				'tags'       => [ 1 ],
			],
			[
				'first_name' => 'Rachel',
				'last_name'  => 'Green',
				'email'      => 'rachel.green@greenfieldagency.com',
				'phone'      => '+1 (503) 555-0274',
				'company'    => 'Greenfield Agency',
				'job_title'  => 'Marketing Manager',
				'city'       => 'Portland',
				'state'      => 'OR',
				'country'    => 'US',
				'status'     => 'active',
				'source'     => 'referral',
				'notes'      => 'Referred by Emily Chen. Evaluating us for ongoing social media management.',
				'tags'       => [],
			],
			[
				'first_name' => 'David',
				'last_name'  => 'Kim',
				'email'      => 'david.kim@kimtechsolutions.com',
				'phone'      => '+1 (206) 555-0463',
				'company'    => 'Kim Tech Solutions',
				'job_title'  => 'Founder',
				'city'       => 'Seattle',
				'state'      => 'WA',
				'country'    => 'US',
				'status'     => 'lead',
				'source'     => 'linkedin',
				'notes'      => 'New startup looking for brand identity package — logo + guidelines + website.',
				'tags'       => [ 1 ],
			],
			[
				'first_name' => 'Sophie',
				'last_name'  => 'Williams',
				'email'      => 'sophie.williams@williamswellness.com',
				'phone'      => '+1 (303) 555-0329',
				'company'    => 'Williams Wellness',
				'job_title'  => 'Owner',
				'city'       => 'Denver',
				'state'      => 'CO',
				'country'    => 'US',
				'status'     => 'customer',
				'source'     => 'google',
				'notes'      => 'Active SEO and blog content client. Monthly retainer. Rank #2 for primary keyword.',
				'tags'       => [ 2 ],
			],
			[
				'first_name' => 'Lucas',
				'last_name'  => 'Bennett',
				'email'      => 'lucas.bennett@bennettpropertygroup.com',
				'phone'      => '+1 (702) 555-0517',
				'company'    => 'Bennett Property Group',
				'job_title'  => 'Director',
				'city'       => 'Las Vegas',
				'state'      => 'NV',
				'country'    => 'US',
				'status'     => 'lead',
				'source'     => 'website',
				'notes'      => 'Interested in e-commerce development for property listings with online booking.',
				'tags'       => [ 1 ],
			],
			[
				'first_name' => 'Olivia',
				'last_name'  => 'Johnson',
				'email'      => 'olivia.johnson@johnsonconsulting.com',
				'phone'      => '+1 (404) 555-0683',
				'company'    => 'Johnson Consulting',
				'job_title'  => 'CEO',
				'city'       => 'Atlanta',
				'state'      => 'GA',
				'country'    => 'US',
				'status'     => 'customer',
				'source'     => 'referral',
				'notes'      => 'Referred by Ethan Brooks. Signed website redesign + SEO contract. Deposit received.',
				'tags'       => [ 0 ],
			],
			[
				'first_name' => 'Noah',
				'last_name'  => 'Carter',
				'email'      => 'noah.carter@cartercafe.com',
				'phone'      => '+1 (512) 555-0741',
				'company'    => 'Carter Café',
				'job_title'  => 'Owner',
				'city'       => 'Austin',
				'state'      => 'TX',
				'country'    => 'US',
				'status'     => 'inactive',
				'source'     => 'google',
				'notes'      => 'Was a client in 2022. Went quiet after initial project. Worth re-engaging.',
				'tags'       => [],
			],
			[
				'first_name' => 'Ava',
				'last_name'  => 'Martinez',
				'email'      => 'ava.martinez@martexgroup.com',
				'phone'      => '+1 (214) 555-0896',
				'company'    => 'Martex Group',
				'job_title'  => 'Marketing Director',
				'city'       => 'Dallas',
				'state'      => 'TX',
				'country'    => 'US',
				'status'     => 'lead',
				'source'     => 'cold_outreach',
				'notes'      => 'Proposal sent for full marketing package. Awaiting CFO sign-off before moving forward.',
				'tags'       => [ 1 ],
			],
			[
				'first_name' => 'Ethan',
				'last_name'  => 'Brooks',
				'email'      => 'ethan.brooks@brooksarchitecture.com',
				'phone'      => '+1 (617) 555-0932',
				'company'    => 'Brooks Architecture',
				'job_title'  => 'Founder',
				'city'       => 'Boston',
				'state'      => 'MA',
				'country'    => 'US',
				'status'     => 'customer',
				'source'     => 'referral',
				'notes'      => 'Brand and web project completed. Excellent client. Strong candidate for an annual retainer pitch.',
				'tags'       => [ 0 ],
			],
			[
				'first_name' => 'Isabella',
				'last_name'  => 'Cooper',
				'email'      => 'isabella.cooper@cooperevents.com',
				'phone'      => '+1 (305) 555-0148',
				'company'    => 'Cooper Events',
				'job_title'  => 'Owner',
				'city'       => 'Miami',
				'state'      => 'FL',
				'country'    => 'US',
				'status'     => 'active',
				'source'     => 'instagram',
				'notes'      => 'Found us via Instagram. Interested in event microsite packages. Exploring options.',
				'tags'       => [],
			],
			[
				'first_name' => 'Mason',
				'last_name'  => 'Reed',
				'email'      => 'mason.reed@reedfinancial.com',
				'phone'      => '+1 (212) 555-0267',
				'company'    => 'Reed Financial Services',
				'job_title'  => 'CEO',
				'city'       => 'New York',
				'state'      => 'NY',
				'country'    => 'US',
				'status'     => 'lead',
				'source'     => 'linkedin',
				'notes'      => 'Large deal in negotiation. Investor portal rebuild — biggest opportunity this quarter.',
				'tags'       => [ 0, 1 ],
			],
			[
				'first_name' => 'Mia',
				'last_name'  => 'Thompson',
				'email'      => 'mia.thompson@thompsonhealthcare.com',
				'phone'      => '+1 (617) 555-0384',
				'company'    => 'Thompson Healthcare',
				'job_title'  => 'Director of Operations',
				'city'       => 'Boston',
				'state'      => 'MA',
				'country'    => 'US',
				'status'     => 'lead',
				'source'     => 'website',
				'notes'      => 'Healthcare patient portal and provider directory project. Qualified and moving to proposal.',
				'tags'       => [ 1 ],
			],
			[
				'first_name' => 'Henry',
				'last_name'  => 'Davis',
				'email'      => 'henry.davis@davisphotography.com',
				'phone'      => '+1 (323) 555-0512',
				'company'    => 'Davis Photography',
				'job_title'  => 'Owner',
				'city'       => 'Los Angeles',
				'state'      => 'CA',
				'country'    => 'US',
				'status'     => 'customer',
				'source'     => 'google',
				'notes'      => 'Portfolio website client. Ongoing hosting and maintenance plan. Low maintenance.',
				'tags'       => [ 2 ],
			],
			[
				'first_name' => 'Charlotte',
				'last_name'  => 'Wilson',
				'email'      => 'charlotte.wilson@wilsonlawgroup.com',
				'phone'      => '+1 (312) 555-0629',
				'company'    => 'Wilson Law Group',
				'job_title'  => 'Founder',
				'city'       => 'Chicago',
				'state'      => 'IL',
				'country'    => 'US',
				'status'     => 'active',
				'source'     => 'referral',
				'notes'      => 'Referred by Sarah Mitchell. Considering a new website and local SEO campaign. Meeting next week.',
				'tags'       => [],
			],
			[
				'first_name' => 'Alexander',
				'last_name'  => 'Moore',
				'email'      => 'alexander.moore@moorelogistics.com',
				'phone'      => '+1 (713) 555-0743',
				'company'    => 'Moore Logistics',
				'job_title'  => 'CEO',
				'city'       => 'Houston',
				'state'      => 'TX',
				'country'    => 'US',
				'status'     => 'lead',
				'source'     => 'trade_show',
				'notes'      => 'Met at a logistics trade show. Wants custom fleet management mobile app. Proposal in preparation.',
				'tags'       => [ 1 ],
			],
			[
				'first_name' => 'Amelia',
				'last_name'  => 'Taylor',
				'email'      => 'amelia.taylor@taylorandco.com',
				'phone'      => '+1 (415) 555-0867',
				'company'    => 'Taylor & Co',
				'job_title'  => 'Marketing Manager',
				'city'       => 'San Francisco',
				'state'      => 'CA',
				'country'    => 'US',
				'status'     => 'customer',
				'source'     => 'referral',
				'notes'      => 'Quarterly brand content client. Consistent work — blog posts and email newsletter graphics.',
				'tags'       => [ 0, 2 ],
			],
			[
				'first_name' => 'William',
				'last_name'  => 'Anderson',
				'email'      => 'william.anderson@andersonplumbing.com',
				'phone'      => '+1 (602) 555-0921',
				'company'    => 'Anderson Plumbing',
				'job_title'  => 'Owner',
				'city'       => 'Phoenix',
				'state'      => 'AZ',
				'country'    => 'US',
				'status'     => 'inactive',
				'source'     => 'google',
				'notes'      => 'Google Ads client in 2022. Paused campaigns due to budget. Check in Q1 next year.',
				'tags'       => [],
			],
		];

		$contacts = [];
		foreach ( $contact_data as $cd ) {
			$tag_indices = $cd['tags'];
			unset( $cd['tags'] );

			$cd['owner_id'] = $owner_id;

			$existing = ContactModel::find_by_email( $cd['email'] );
			if ( $existing ) {
				$contacts[] = $existing;
				continue;
			}

			$contact    = ContactModel::create( $cd );
			$contacts[] = $contact;
			$created_ids['contacts'][] = (int) $contact->id;

			// Tag as sample data so delete_sample_data() can find it regardless of email changes.
			global $wpdb;
			// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.SlowDBQuery.slow_db_query_meta_key, WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- INSERT into plugin-owned custom meta table, not a SELECT; no query slowdown.
			$wpdb->insert(
				$wpdb->prefix . 'pcrm_contact_meta',
				[
					'contact_id' => (int) $contact->id,
					'meta_key'   => '_is_sample_data',
					'meta_value' => '1',
				],
				[ '%d', '%s', '%s' ]
			);
			// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.SlowDBQuery.slow_db_query_meta_key, WordPress.DB.SlowDBQuery.slow_db_query_meta_value

			foreach ( $tag_indices as $idx ) {
				if ( isset( $tags[ $idx ] ) ) {
					$contact->attach_tag( (int) $tags[ $idx ]->id );
				}
			}
		}

		return $contacts;
	}

	private function seed_deals( array $contacts, array $stages, int $pipeline_id, string $currency, int $owner_id, array &$created_ids ): void {
		$open_stages = array_values( array_filter( $stages, fn( $s ) => 'won' !== $s->type && 'lost' !== $s->type ) );

		// contact_idx matches the order in seed_contacts(): 0=Sarah Mitchell, 1=James Harrington,
		// 2=Emily Chen, 3=Michael Torres, 5=David Kim, 6=Sophie Williams, 7=Lucas Bennett,
		// 8=Olivia Johnson, 10=Ava Martinez, 11=Ethan Brooks, 13=Mason Reed, 14=Mia Thompson,
		// 17=Alexander Moore, 18=Amelia Taylor.
		$deal_data = [
			[
				'title'          => 'Investor Portal Rebuild',
				'value'          => 42000.00,
				'contact_idx'    => 13,
				'stage_idx'      => min( 2, count( $open_stages ) - 1 ),
				'expected_close' => gmdate( 'Y-m-d', strtotime( '+21 days' ) ),
			],
			[
				'title'          => 'Healthcare Portal & Provider Directory',
				'value'          => 28500.00,
				'contact_idx'    => 14,
				'stage_idx'      => min( 2, count( $open_stages ) - 1 ),
				'expected_close' => gmdate( 'Y-m-d', strtotime( '+30 days' ) ),
			],
			[
				'title'          => 'Fleet Management Mobile App',
				'value'          => 35000.00,
				'contact_idx'    => 17,
				'stage_idx'      => min( 1, count( $open_stages ) - 1 ),
				'expected_close' => gmdate( 'Y-m-d', strtotime( '+45 days' ) ),
			],
			[
				'title'          => 'Full Site Redesign',
				'value'          => 12000.00,
				'contact_idx'    => 3,
				'stage_idx'      => min( 1, count( $open_stages ) - 1 ),
				'expected_close' => gmdate( 'Y-m-d', strtotime( '+30 days' ) ),
			],
			[
				'title'          => 'Full Marketing Package',
				'value'          => 18000.00,
				'contact_idx'    => 10,
				'stage_idx'      => min( 2, count( $open_stages ) - 1 ),
				'expected_close' => gmdate( 'Y-m-d', strtotime( '+14 days' ) ),
			],
			[
				'title'          => 'SEO Retainer — 12 Month',
				'value'          => 9600.00,
				'contact_idx'    => 1,
				'stage_idx'      => min( 1, count( $open_stages ) - 1 ),
				'expected_close' => gmdate( 'Y-m-d', strtotime( '+20 days' ) ),
			],
			[
				'title'          => 'Brand Identity Package',
				'value'          => 7500.00,
				'contact_idx'    => 5,
				'stage_idx'      => 0,
				'expected_close' => gmdate( 'Y-m-d', strtotime( '+35 days' ) ),
			],
			[
				'title'          => 'E-commerce Property Listings',
				'value'          => 22000.00,
				'contact_idx'    => 7,
				'stage_idx'      => 0,
				'expected_close' => gmdate( 'Y-m-d', strtotime( '+50 days' ) ),
			],
			[
				'title'          => 'Website Redesign + SEO',
				'value'          => 14500.00,
				'contact_idx'    => 8,
				'stage_idx'      => min( 2, count( $open_stages ) - 1 ),
				'expected_close' => gmdate( 'Y-m-d', strtotime( '+7 days' ) ),
			],
			[
				'title'          => 'Annual Retainer — Brand & Content',
				'value'          => 19200.00,
				'contact_idx'    => 18,
				'stage_idx'      => min( 2, count( $open_stages ) - 1 ),
				'expected_close' => gmdate( 'Y-m-d', strtotime( '+10 days' ) ),
			],
		];

		$position = 1000.0;
		foreach ( $deal_data as $dd ) {
			$stage = $open_stages[ $dd['stage_idx'] ] ?? $open_stages[0];

			$deal = DealModel::create( [
				'title'          => $dd['title'],
				'value'          => $dd['value'],
				'contact_id'     => (int) $contacts[ $dd['contact_idx'] ]->id,
				'pipeline_id'    => $pipeline_id,
				'stage_id'       => (int) $stage->id,
				'owner_id'       => $owner_id,
				'currency'       => $currency,
				'expected_close' => $dd['expected_close'],
				'status'         => 'open',
				'position'       => $position,
			] );

			$created_ids['deals'][] = (int) $deal->id;
			$position += 1000.0;
		}
	}

	private function seed_tasks( array $contacts, int $owner_id, array &$created_ids ): void {
		$task_data = [
			[
				'title'       => 'Call Mason Reed — confirm investor portal scope',
				'type'        => 'call',
				'priority'    => 'high',
				'contact_idx' => 13,
				'due_offset'  => '+1 day',
			],
			[
				'title'       => 'Send proposal to Ava Martinez',
				'type'        => 'email',
				'priority'    => 'high',
				'contact_idx' => 10,
				'due_offset'  => '+1 day',
			],
			[
				'title'       => 'Send SEO case studies to James Harrington',
				'type'        => 'email',
				'priority'    => 'medium',
				'contact_idx' => 1,
				'due_offset'  => '+2 days',
			],
			[
				'title'       => 'Follow up with Mia Thompson re: healthcare portal proposal',
				'type'        => 'follow_up',
				'priority'    => 'high',
				'contact_idx' => 14,
				'due_offset'  => '+3 days',
			],
			[
				'title'       => 'Meeting with Charlotte Wilson — website & SEO brief',
				'type'        => 'meeting',
				'priority'    => 'medium',
				'contact_idx' => 16,
				'due_offset'  => '+5 days',
			],
			[
				'title'       => 'Re-engage Noah Carter — check in call',
				'type'        => 'call',
				'priority'    => 'low',
				'contact_idx' => 9,
				'due_offset'  => '+7 days',
			],
		];

		foreach ( $task_data as $td ) {
			$task = TaskModel::create( [
				'title'      => $td['title'],
				'type'       => $td['type'],
				'priority'   => $td['priority'],
				'status'     => 'pending',
				'contact_id' => (int) $contacts[ $td['contact_idx'] ]->id,
				'owner_id'   => $owner_id,
				'due_date'   => gmdate( 'Y-m-d H:i:s', strtotime( $td['due_offset'] ) ),
			] );

			$created_ids['tasks'][] = (int) $task->id;
		}
	}
}
