<?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 WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

class RestTasks extends Controller {

	protected $rest_base = 'tasks';

	public function register_routes(): void {
		$task_write_args = [
			'title'       => [ 'type' => 'string',  'required' => false, 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg' ],
			'description' => [ 'type' => 'string',  'required' => false, 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg' ],
			'type'        => [ 'type' => 'string',  'required' => false, 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg' ],
			'status'      => [ 'type' => 'string',  'required' => false, 'enum' => [ 'pending', 'in_progress', 'completed' ], 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg' ],
			'priority'    => [ 'type' => 'string',  'required' => false, 'enum' => [ 'low', 'medium', 'high' ], 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg' ],
			'contact_id'  => [ 'type' => 'integer', 'required' => false, 'sanitize_callback' => 'absint',              'validate_callback' => 'rest_validate_request_arg' ],
			'deal_id'     => [ 'type' => 'integer', 'required' => false, 'sanitize_callback' => 'absint',              'validate_callback' => 'rest_validate_request_arg' ],
			'owner_id'    => [ 'type' => 'integer', 'required' => false, 'sanitize_callback' => 'absint',              'validate_callback' => 'rest_validate_request_arg' ],
			'due_date'    => [ 'type' => 'string',  'required' => false, 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg' ],
		];

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base,
			[
				[
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => [ $this, 'index' ],
					'permission_callback' => [ $this, 'require_contacts_cap' ],
					'args'                => [
						'page'       => [ 'type' => 'integer', 'default' => 1,  'required' => false, 'sanitize_callback' => 'absint',             'validate_callback' => 'rest_validate_request_arg' ],
						'per_page'   => [ 'type' => 'integer', 'default' => 20, 'required' => false, 'sanitize_callback' => 'absint',             'validate_callback' => 'rest_validate_request_arg' ],
						'status'     => [ 'type' => 'string',                   'required' => false, 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg' ],
						'owner_id'   => [ 'type' => 'integer',                  'required' => false, 'sanitize_callback' => 'absint',             'validate_callback' => 'rest_validate_request_arg' ],
						'contact_id' => [ 'type' => 'integer',                  'required' => false, 'sanitize_callback' => 'absint',             'validate_callback' => 'rest_validate_request_arg' ],
						'deal_id'    => [ 'type' => 'integer',                  'required' => false, 'sanitize_callback' => 'absint',             'validate_callback' => 'rest_validate_request_arg' ],
						'due_before' => [ 'type' => 'string',                   'required' => false, 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg' ],
						'due_after'  => [ 'type' => 'string',                   'required' => false, 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg' ],
					],
				],
				[
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => [ $this, 'create_item' ],
					'permission_callback' => [ $this, 'require_contacts_cap' ],
					'args'                => $task_write_args,
				],
			]
		);

		// Static route — registered before (?P<id>\d+) so "export" is not treated as an ID.
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/export',
			[
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => [ $this, 'export_csv' ],
				'permission_callback' => [ $this, 'require_contacts_cap' ],
				'args'                => [
					'status'     => [ 'type' => 'string',  'required' => false, 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg' ],
					'owner_id'   => [ 'type' => 'integer', 'required' => false, 'sanitize_callback' => 'absint',             'validate_callback' => 'rest_validate_request_arg' ],
					'contact_id' => [ 'type' => 'integer', 'required' => false, 'sanitize_callback' => 'absint',             'validate_callback' => 'rest_validate_request_arg' ],
					'deal_id'    => [ 'type' => 'integer', 'required' => false, 'sanitize_callback' => 'absint',             'validate_callback' => 'rest_validate_request_arg' ],
				],
			]
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<id>\d+)',
			[
				[
					'methods'             => WP_REST_Server::EDITABLE,
					'callback'            => [ $this, 'update_item' ],
					'permission_callback' => [ $this, 'require_contacts_cap' ],
					'args'                => array_merge(
						[ 'id' => [ 'type' => 'integer', 'required' => true, 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg' ] ],
						$task_write_args
					),
				],
				[
					'methods'             => WP_REST_Server::DELETABLE,
					'callback'            => [ $this, 'delete_item' ],
					'permission_callback' => [ $this, 'require_contacts_cap' ],
					'args'                => [
						'id' => [ 'type' => 'integer', 'required' => true, 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg' ],
					],
				],
			]
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<id>\d+)/complete',
			[
				'methods'             => WP_REST_Server::EDITABLE,
				'callback'            => [ $this, 'complete' ],
				'permission_callback' => [ $this, 'require_contacts_cap' ],
				'args'                => [
					'id' => [ 'type' => 'integer', 'required' => true, 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg' ],
				],
			]
		);
	}

	public function index( WP_REST_Request $request ): WP_REST_Response {
		$pagination = $this->get_pagination_params( $request );
		$status     = $this->get_string_param( $request, 'status' );
		$owner_id   = $this->get_int_param( $request, 'owner_id' );
		$contact_id = $this->get_int_param( $request, 'contact_id' );
		$deal_id    = $this->get_int_param( $request, 'deal_id' );
		$due_before = $this->get_string_param( $request, 'due_before' );
		if ( $due_before && ! strtotime( $due_before ) ) {
			return $this->bad_request( 'invalid_date', __( 'Invalid due_before date format.', 'pressio-crm' ) );
		}

		$due_after = $this->get_string_param( $request, 'due_after' );
		if ( $due_after && ! strtotime( $due_after ) ) {
			return $this->bad_request( 'invalid_date', __( 'Invalid due_after date format.', 'pressio-crm' ) );
		}

		$query = TaskModel::query()
			->not_deleted()
			->where_if( $status, 'status' )
			->where_if( $owner_id ?: null, 'owner_id', '%d' )
			->where_if( $contact_id ?: null, 'contact_id', '%d' )
			->where_if( $deal_id ?: null, 'deal_id', '%d' )
			->order_by( 'due_date', 'ASC' );

		if ( $due_before ) {
			$query->where_raw( '`due_date` <= %s', [ $due_before ] );
		}

		if ( $due_after ) {
			$query->where_raw( '`due_date` >= %s', [ $due_after ] );
		}

		$result = $query->paginate( $pagination['per_page'], $pagination['page'] );

		return $this->paginated_response( $result, [ $this, 'format_task' ] );
	}

	public function create_item( $request ) {
		$title = $this->get_string_param( $request, 'title' );

		if ( empty( $title ) ) {
			return $this->validation_error( [ 'title' => __( 'title is required.', 'pressio-crm' ) ] );
		}

		$due_date = $request->get_param( 'due_date' );
		if ( null !== $due_date ) {
			$clean = sanitize_text_field( wp_unslash( $due_date ) );
			if ( $clean !== '' && ! strtotime( $clean ) ) {
				return $this->validation_error( [ 'due_date' => __( 'due_date must be a valid date.', 'pressio-crm' ) ] );
			}
		}

		$data = $this->sanitise_task_fields( $request );

		try {
			$task = TaskModel::create( $data );
		} catch ( \RuntimeException $e ) {
			return $this->bad_request( 'pressio_crm_create_failed', $e->getMessage() );
		}

		return $this->created( $this->format_task( $task ) );
	}

	public function update_item( $request ) {
		$task = TaskModel::find( absint( $request->get_param( 'id' ) ) );

		if ( null === $task ) {
			return $this->not_found();
		}

		$due_date = $request->get_param( 'due_date' );
		if ( null !== $due_date ) {
			$clean = sanitize_text_field( wp_unslash( $due_date ) );
			if ( $clean !== '' && ! strtotime( $clean ) ) {
				return $this->validation_error( [ 'due_date' => __( 'due_date must be a valid date.', 'pressio-crm' ) ] );
			}
		}

		$data = $this->sanitise_task_fields( $request );
		$task->update( $data );

		return $this->ok( $this->format_task( $task ) );
	}

	public function delete_item( $request ) {
		$task = TaskModel::find( absint( $request->get_param( 'id' ) ) );

		if ( null === $task ) {
			return $this->not_found();
		}

		$task->delete();

		return $this->no_content();
	}

	public function complete( WP_REST_Request $request ) {
		$task = TaskModel::find( absint( $request->get_param( 'id' ) ) );

		if ( null === $task ) {
			return $this->not_found();
		}

		$task->complete();

		return $this->ok( $this->format_task( $task ) );
	}

	public function format_task( object $task ): array {
		return [
			'id'           => (int) $task->id,
			'contact_id'   => (int) $task->contact_id,
			'deal_id'      => (int) $task->deal_id,
			'owner_id'     => (int) $task->owner_id,
			'title'        => (string) $task->title,
			'description'  => (string) $task->description,
			'type'         => (string) $task->type,
			'status'       => (string) $task->status,
			'priority'     => (string) $task->priority,
			'due_date'     => (string) $task->due_date,
			'completed_at' => (string) $task->completed_at,
			'created_at'   => (string) $task->created_at,
			'updated_at'   => (string) $task->updated_at,
		];
	}

	public function export_csv( WP_REST_Request $request ): WP_REST_Response {
		$status      = $this->get_string_param( $request, 'status' );
		$owner_id    = $this->get_int_param( $request, 'owner_id' );
		$contact_id  = $this->get_int_param( $request, 'contact_id' );
		$deal_id     = $this->get_int_param( $request, 'deal_id' );

		// Fetch all matching non-deleted tasks in one query, capped at 10 000 rows.
		$tasks = TaskModel::query()
			->not_deleted()
			->where_if( $status, 'status' )
			->where_if( $owner_id ?: null, 'owner_id', '%d' )
			->where_if( $contact_id ?: null, 'contact_id', '%d' )
			->where_if( $deal_id ?: null, 'deal_id', '%d' )
			->order_by( 'due_date', 'ASC' )
			->limit( 10000 )
			->get();

		if ( empty( $tasks ) ) {
			return $this->ok( [ 'csv' => '', 'filename' => 'tasks-' . gmdate( 'Y-m-d' ) . '.csv' ] );
		}

		// --- Batch-fetch contacts (name + email lookup) ---
		$contact_ids  = array_unique( array_filter( array_map( static fn( $t ) => (int) $t->contact_id, $tasks ) ) );
		$contacts_map = [];
		if ( ! empty( $contact_ids ) ) {
			$contacts = ContactModel::query()
				->select( 'id, first_name, last_name, email' )
				->where_in( 'id', $contact_ids, '%d' )
				->not_deleted()
				->get();
			foreach ( $contacts as $c ) {
				$contacts_map[ (int) $c->id ] = $c;
			}
		}

		// --- Batch-fetch deals (title lookup) ---
		$deal_ids  = array_unique( array_filter( array_map( static fn( $t ) => (int) $t->deal_id, $tasks ) ) );
		$deals_map = [];
		if ( ! empty( $deal_ids ) ) {
			$deals = DealModel::query()
				->select( 'id, title' )
				->where_in( 'id', $deal_ids, '%d' )
				->not_deleted()
				->get();
			foreach ( $deals as $d ) {
				$deals_map[ (int) $d->id ] = (string) $d->title;
			}
		}

		// Build CSV header + rows.
		$columns = [
			'id', 'title', 'type', 'priority', 'status',
			'due_date', 'completed_at', 'contact_name', 'contact_email',
			'deal_title', 'owner_id', 'created_at',
		];

		$rows   = [];
		$rows[] = $columns;

		foreach ( $tasks as $task ) {
			$contact      = $contacts_map[ (int) $task->contact_id ] ?? null;
			$contact_name = '';
			if ( null !== $contact ) {
				$contact_name = trim( esc_html( $contact->first_name ) . ' ' . esc_html( $contact->last_name ) );
			}

			$rows[] = [
				(int) $task->id,
				esc_html( (string) $task->title ),
				esc_html( (string) $task->type ),
				esc_html( (string) $task->priority ),
				esc_html( (string) $task->status ),
				esc_html( (string) $task->due_date ),
				esc_html( (string) $task->completed_at ),
				$contact_name,
				esc_html( $contact->email ?? '' ),
				esc_html( $deals_map[ (int) $task->deal_id ] ?? '' ),
				(int) $task->owner_id,
				esc_html( (string) $task->created_at ),
			];
		}

		$csv = '';
		foreach ( $rows as $row ) {
			$csv .= $this->format_csv_row( $row );
		}

		return $this->ok( [
			'csv'      => $csv,
			'filename' => 'tasks-' . gmdate( 'Y-m-d' ) . '.csv',
		] );
	}

	/**
	 * Format a single array of values as a CSV line.
	 * Wraps cells in double-quotes when they contain commas, quotes, or newlines.
	 */
	private function format_csv_row( array $cells ): string {
		$escaped = array_map( static function ( $cell ) {
			$cell = (string) $cell;
			if ( str_contains( $cell, ',' ) || str_contains( $cell, '"' ) || str_contains( $cell, "\n" ) ) {
				$cell = '"' . str_replace( '"', '""', $cell ) . '"';
			}
			return $cell;
		}, $cells );

		return implode( ',', $escaped ) . "\n";
	}

	private function sanitise_task_fields( WP_REST_Request $request ): array {
		$data = [];

		$string_fields = [ 'title', 'description', 'type' ];
		foreach ( $string_fields as $field ) {
			$value = $request->get_param( $field );
			if ( null !== $value ) {
				$data[ $field ] = sanitize_text_field( wp_unslash( $value ) );
			}
		}

		$status = $request->get_param( 'status' );
		if ( null !== $status ) {
			$allowed = [ 'pending', 'in_progress', 'completed' ];
			$clean   = sanitize_text_field( wp_unslash( $status ) );
			if ( in_array( $clean, $allowed, true ) ) {
				$data['status'] = $clean;
			}
		}

		$priority = $request->get_param( 'priority' );
		if ( null !== $priority ) {
			$allowed  = [ 'low', 'medium', 'high' ];
			$clean    = sanitize_text_field( wp_unslash( $priority ) );
			if ( in_array( $clean, $allowed, true ) ) {
				$data['priority'] = $clean;
			}
		}

		$int_fields = [ 'contact_id', 'deal_id', 'owner_id' ];
		foreach ( $int_fields as $field ) {
			$value = $request->get_param( $field );
			if ( null !== $value ) {
				$data[ $field ] = absint( $value );
			}
		}

		$due_date = $request->get_param( 'due_date' );
		if ( null !== $due_date ) {
			$clean            = sanitize_text_field( wp_unslash( $due_date ) );
			$data['due_date'] = $clean ?: null;
		}

		return $data;
	}
}
