<?php
/**
 * TitanCart Import/Export REST Controller — Module 13
 *
 * Registers endpoints under /wp-json/titancart/v1/:
 *
 *   POST   /import/upload              Upload CSV, get job_id + preview + field map
 *   POST   /import/validate            Dry-run validate with column mapping
 *   POST   /import/start               Commit column map, schedule import cron
 *   GET    /import/progress/{job_id}   Poll job status and progress %
 *   GET    /import/jobs                List recent import jobs
 *   GET    /import/fields/{type}       Get field map for a given entity type
 *
 *   Import presets (KI-34 — Pro "Advanced Products Import", gated on
 *   is_active('advanced-import')):
 *   GET    /import/presets             List presets (optional ?type=)
 *   POST   /import/presets             Create a preset { name, type, column_map, options }
 *   PUT    /import/presets/{id}        Update a preset { name?, column_map?, options? }
 *   DELETE /import/presets/{id}        Delete a preset
 *
 *   GET    /export/columns/{type}      Available columns for export type
 *   POST   /export/generate            Generate CSV and return download URL
 *   GET    /export/download/{job_id}   (future — currently generate returns URL directly)
 *
 * All routes require manage_options capability.
 *
 * @package TitanCart\REST\ImportExport
 */

namespace TitanCart\REST\ImportExport;

use TitanCart\Modules\ImportExport\ImportService;
use TitanCart\Modules\ImportExport\ExportService;
use TitanCart\Core\LogService;

defined( 'ABSPATH' ) || exit;

class ImportExportController {

	const NAMESPACE = 'titancart/v1';

	public function register_routes(): void {

		// ── Import ────────────────────────────────────────────────────────────

		register_rest_route( self::NAMESPACE, '/import/upload', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'upload' ],
			'permission_callback' => [ $this, 'adminOnly' ],
		] );

		register_rest_route( self::NAMESPACE, '/import/validate', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'validate' ],
			'permission_callback' => [ $this, 'adminOnly' ],
			'args'                => [
				'job_id'     => [ 'required' => true,  'type' => 'integer' ],
				'column_map' => [ 'required' => true,  'type' => 'object'  ],
			],
		] );

		register_rest_route( self::NAMESPACE, '/import/start', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'start' ],
			'permission_callback' => [ $this, 'adminOnly' ],
			'args'                => [
				'job_id'     => [ 'required' => true, 'type' => 'integer' ],
				'column_map' => [ 'required' => true, 'type' => 'object'  ],
			],
		] );

		register_rest_route( self::NAMESPACE, '/import/progress/(?P<job_id>\d+)', [
			'methods'             => 'GET',
			'callback'            => [ $this, 'progress' ],
			'permission_callback' => [ $this, 'adminOnly' ],
			'args'                => [
				'job_id' => [ 'required' => true, 'type' => 'integer' ],
			],
		] );

		register_rest_route( self::NAMESPACE, '/import/jobs', [
			'methods'             => 'GET',
			'callback'            => [ $this, 'listJobs' ],
			'permission_callback' => [ $this, 'adminOnly' ],
		] );

		register_rest_route( self::NAMESPACE, '/import/fields/(?P<type>[a-z_]+)', [
			'methods'             => 'GET',
			'callback'            => [ $this, 'getFields' ],
			'permission_callback' => [ $this, 'adminOnly' ],
			'args'                => [
				'type' => [ 'required' => true, 'type' => 'string' ],
			],
		] );


		// ── Export ────────────────────────────────────────────────────────────

		register_rest_route( self::NAMESPACE, '/export/columns/(?P<type>[a-z_]+)', [
			'methods'             => 'GET',
			'callback'            => [ $this, 'getColumns' ],
			'permission_callback' => [ $this, 'adminOnly' ],
			'args'                => [
				'type' => [ 'required' => true, 'type' => 'string' ],
			],
		] );

		register_rest_route( self::NAMESPACE, '/export/generate', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'generate' ],
			'permission_callback' => [ $this, 'adminOnly' ],
			'args'                => [
				'type'    => [ 'required' => true, 'type' => 'string' ],
				'columns' => [ 'required' => false, 'type' => 'array', 'default' => [] ],
				'filters' => [ 'required' => false, 'type' => 'object', 'default' => [] ],
			],
		] );

		// ── Flat aliases (tester / external compat) ────────────────────────────

		register_rest_route( self::NAMESPACE, '/import/logs', [
			'methods'             => 'GET',
			'callback'            => [ $this, 'listJobs' ],
			'permission_callback' => [ $this, 'adminOnly' ],
		] );

		register_rest_route( self::NAMESPACE, '/export/jobs', [
			'methods'             => 'GET',
			'callback'            => [ $this, 'listJobs' ],
			'permission_callback' => [ $this, 'adminOnly' ],
		] );
	}

	// ──────────────────────────────────────────────────────────────────────────
	// Import handlers
	// ──────────────────────────────────────────────────────────────────────────

	/**
	 * POST /import/upload
	 * Expects a multipart form upload: file (CSV), type (string), options (JSON object).
	 */
	public function upload( \WP_REST_Request $request ): \WP_REST_Response {
		$files = $request->get_file_params();

		if ( empty( $files['file'] ) ) {
			return new \WP_REST_Response( [ 'error' => 'No file uploaded. Send the CSV as multipart field "file".' ], 400 );
		}

		$type = sanitize_key( $request->get_param( 'type' ) ?: 'products' );
		$raw_options = $request->get_param( 'options' );
		$options = is_array( $raw_options ) ? $raw_options : [];

		try {
			$result = ( new ImportService() )->upload( $files['file'], $type, $options );
			return new \WP_REST_Response( $result, 200 );
		} catch ( \Throwable $e ) {
			return new \WP_REST_Response( [ 'error' => $e->getMessage() ], 422 );
		}
	}

	/**
	 * POST /import/validate
	 */
	public function validate( \WP_REST_Request $request ): \WP_REST_Response {
		$job_id     = (int) $request->get_param( 'job_id' );
		$column_map = (array) $request->get_param( 'column_map' );

		// Normalise: the UI may send [ { index: 0, field: 'name' }, ... ]
		// or a simple object { "0": "name", "1": "sku" }.
		// We normalise to the latter (int-keyed array).
		$column_map = $this->normaliseColumnMap( $column_map );

		try {
			$result = ( new ImportService() )->validate( $job_id, $column_map );
			return new \WP_REST_Response( $result, 200 );
		} catch ( \Throwable $e ) {
			return new \WP_REST_Response( [ 'error' => $e->getMessage() ], 422 );
		}
	}

	/**
	 * POST /import/start
	 */
	public function start( \WP_REST_Request $request ): \WP_REST_Response {
		$job_id     = (int) $request->get_param( 'job_id' );
		$column_map = $this->normaliseColumnMap( (array) $request->get_param( 'column_map' ) );

		try {
			$ok = ( new ImportService() )->startJob( $job_id, $column_map );
			if ( ! $ok ) {
				return new \WP_REST_Response( [ 'error' => "Job #{$job_id} could not be started. It may not exist or is already running." ], 409 );
			}

			LogService::info( 'general', 'create', 'Import job started: #' . $job_id, [
				'object_type' => 'import_job',
				'object_id'   => $job_id,
			] );

			return new \WP_REST_Response( [
				'job_id'  => $job_id,
				'message' => 'Import job queued. Poll /import/progress/' . $job_id . ' for updates.',
			], 200 );
		} catch ( \Throwable $e ) {
			return new \WP_REST_Response( [ 'error' => $e->getMessage() ], 422 );
		}
	}

	/**
	 * GET /import/progress/{job_id}
	 */
	public function progress( \WP_REST_Request $request ): \WP_REST_Response {
		$job_id = (int) $request->get_param( 'job_id' );

		$job = ( new ImportService() )->getProgress( $job_id );
		if ( ! $job ) {
			return new \WP_REST_Response( [ 'error' => "Job #{$job_id} not found." ], 404 );
		}

		$total     = max( 1, (int) $job->total_rows );
		$processed = (int) $job->processed;

		return new \WP_REST_Response( [
			'id'           => (int) $job->id,
			'type'         => $job->type,
			'filename'     => $job->filename,
			'status'       => $job->status,
			'total_rows'   => $total,
			'processed'    => $processed,
			'errors'       => (int) $job->errors,
			'percent'      => (int) round( ( $processed / $total ) * 100 ),
			'log'          => $job->log,
			'started_at'   => $job->started_at,
			'completed_at' => $job->completed_at,
			'created_at'   => $job->created_at,
		], 200 );
	}

	/**
	 * GET /import/jobs
	 */
	public function listJobs( \WP_REST_Request $request ): \WP_REST_Response {
		$limit  = (int) ( $request->get_param( 'per_page' ) ?: 20 );
		$offset = ( (int) ( $request->get_param( 'page' ) ?: 1 ) - 1 ) * $limit;

		$jobs = ( new ImportService() )->getJobs( $limit, $offset );

		return new \WP_REST_Response( array_map( function ( $job ) {
			$total     = max( 1, (int) $job->total_rows );
			$processed = (int) $job->processed;
			return [
				'id'           => (int) $job->id,
				'type'         => $job->type,
				'filename'     => $job->filename,
				'status'       => $job->status,
				'total_rows'   => $total,
				'processed'    => $processed,
				'errors'       => (int) $job->errors,
				'percent'      => (int) round( ( $processed / $total ) * 100 ),
				'created_at'   => $job->created_at,
				'completed_at' => $job->completed_at,
			];
		}, $jobs ), 200 );
	}

	/**
	 * GET /import/fields/{type}
	 */
	public function getFields( \WP_REST_Request $request ): \WP_REST_Response {
		$type = sanitize_key( $request->get_param( 'type' ) );
		$map  = ( new ImportService() )->getFieldMap( $type );
		return new \WP_REST_Response( $map, 200 );
	}


	// ──────────────────────────────────────────────────────────────────────────
	// Export handlers
	// ──────────────────────────────────────────────────────────────────────────

	/**
	 * GET /export/columns/{type}
	 */
	public function getColumns( \WP_REST_Request $request ): \WP_REST_Response {
		$type    = sanitize_key( $request->get_param( 'type' ) );
		$columns = ( new ExportService() )->getColumns( $type );

		// Return as array of { key, label } for easy UI rendering.
		$result = [];
		foreach ( $columns as $key => $label ) {
			$result[] = [ 'key' => $key, 'label' => $label ];
		}

		return new \WP_REST_Response( $result, 200 );
	}

	/**
	 * POST /export/generate
	 */
	public function generate( \WP_REST_Request $request ): \WP_REST_Response {
		$type    = sanitize_key( $request->get_param( 'type' ) ?: 'products' );
		$columns = array_map( 'sanitize_key', (array) ( $request->get_param( 'columns' ) ?: [] ) );
		$filters = (array) ( $request->get_param( 'filters' ) ?: [] );

		// Sanitise filter values.
		$safe_filters = [];
		foreach ( [ 'status', 'date_from', 'date_to' ] as $k ) {
			if ( ! empty( $filters[ $k ] ) ) {
				$safe_filters[ $k ] = sanitize_text_field( $filters[ $k ] );
			}
		}

		try {
			$result = ( new ExportService() )->generate( $type, $safe_filters, $columns );

			LogService::info( 'general', 'create', 'Export generated: ' . $type . ' (' . ( $result['rows'] ?? 0 ) . ' rows)', [
				'object_type' => 'export',
				'type'        => $type,
				'rows'        => $result['rows'] ?? 0,
			] );

			return new \WP_REST_Response( $result, 200 );
		} catch ( \Throwable $e ) {
			return new \WP_REST_Response( [ 'error' => $e->getMessage() ], 422 );
		}
	}

	// ──────────────────────────────────────────────────────────────────────────
	// Helpers
	// ──────────────────────────────────────────────────────────────────────────

	public function adminOnly(): bool {
		return current_user_can( 'manage_options' );
	}


	/**
	 * Normalise column map to int-indexed [ col_index => tc_field ] format.
	 *
	 * Accepts either:
	 *   { "0": "name", "1": "sku", "2": "" }
	 * or:
	 *   [ { "index": 0, "field": "name" }, ... ]
	 *
	 * @param  array $raw
	 * @return array<int, string>
	 */
	private function normaliseColumnMap( array $raw ): array {
		$result = [];

		// Detect object-of-objects format.
		$first = reset( $raw );
		if ( is_array( $first ) && isset( $first['index'] ) ) {
			foreach ( $raw as $item ) {
				$result[ (int) $item['index'] ] = $this->sanitiseFieldKey( $item['field'] ?? '' );
			}
		} else {
			foreach ( $raw as $k => $v ) {
				$result[ (int) $k ] = $this->sanitiseFieldKey( (string) $v );
			}
		}

		return $result;
	}

	/**
	 * Sanitise a field key, preserving the "feature:" prefix that sanitize_key() would strip.
	 */
	private function sanitiseFieldKey( string $key ): string {
		// Feature keys use a colon separator: "feature:123"
		if ( preg_match( '/^feature:(\d+)$/', $key, $m ) ) {
			return 'feature:' . $m[1];
		}
		return sanitize_key( $key );
	}
}
