<?php
/**
 * AEAT Subsanation Service
 *
 * Main orchestrator for the AEAT error handling layer.
 * This is the primary entry point for processing AEAT responses.
 *
 * @package Freefactu
 * @since 1.1.0
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class Freefactu_Aeat_Subsanation_Service
 *
 * Orchestrates the error handling flow:
 * 1. Parse AEAT response
 * 2. Classify error
 * 3. Decide next action
 * 4. Update database
 * 5. Return result with user message
 */
class Freefactu_Aeat_Subsanation_Service {

	/**
	 * Process an AEAT response for an invoice.
	 *
	 * This is the main entry point for error handling.
	 * Call this after every AEAT submission.
	 *
	 * @param array  $invoice_data Invoice context array with keys:
	 *                             - invoice_id: int
	 *                             - order_id: int
	 *                             - tipo_factura: string (F1, F2, F3, F4, R1-R5)
	 *                             - invoice_number: string
	 * @param object $library_response Response from josemmo/verifactu-php library.
	 * @param string $raw_xml          Raw XML response from AEAT (optional).
	 * @return array Result array with keys:
	 *               - success: bool
	 *               - next_action: string (Freefactu_Aeat_Next_Action constant)
	 *               - user_message: string (Spanish)
	 *               - error_category: string (Freefactu_Aeat_Error_Categories constant)
	 *               - can_auto_retry: bool
	 *               - response: Freefactu_Aeat_Response object
	 */
	public function process_aeat_response( array $invoice_data, $library_response, $raw_xml = '' ) {
		// 1. Parse and normalize response
		$response = Freefactu_Aeat_Response::from_library_response( $library_response, $raw_xml );

		// 2. Classify the error (if any)
		$response = Freefactu_Aeat_Error_Classifier::classify_response( $response );

		// 3. Load additional invoice context from database if available
		$invoice_data = $this->enrich_invoice_data( $invoice_data );

		// 4. Decide next action
		$decision = Freefactu_Aeat_Next_Action_Decider::decide( $invoice_data, $response );

		// 5. Update database with new state
		$this->update_invoice_state(
			$invoice_data,
			$response,
			$decision
		);

		// 6. Log the decision
		$this->log_decision( $invoice_data, $response, $decision );

		// 7. Fire action hook for extensibility
		do_action( 'freefactu_aeat_response_processed', $invoice_data, $response, $decision );

		// 8. Return structured result
		return array(
			'success'        => $response->is_success(),
			'next_action'    => $decision['next_action'],
			'user_message'   => $decision['user_message'],
			'error_category' => $response->get_error_category(),
			'error_code'     => $response->get_error_code(),
			'can_auto_retry' => $decision['can_auto_retry'],
			'action_label'   => $decision['action_label'],
			'instruction'    => $decision['instruction'],
			'response'       => $response,
			'csv'            => $response->get_csv(),
		);
	}

	/**
	 * Enrich invoice data with database values.
	 *
	 * @param array $invoice_data Basic invoice data.
	 * @return array Enriched invoice data.
	 */
	private function enrich_invoice_data( array $invoice_data ) {
		// If we have an invoice_id or order_id, load current state from DB
		if ( ! empty( $invoice_data['invoice_id'] ) ) {
			$invoice = Freefactu_DB::get_invoice_by_id( $invoice_data['invoice_id'] );
		} elseif ( ! empty( $invoice_data['order_id'] ) ) {
			$invoice = Freefactu_DB::get_invoice_by_order_id( $invoice_data['order_id'] );
		} else {
			$invoice = null;
		}

		if ( $invoice ) {
			// Merge database values with provided data
			$invoice_data = array_merge( array(
				'invoice_id'        => $invoice->id,
				'order_id'          => $invoice->order_id,
				'invoice_number'    => $invoice->invoice_number,
				'tipo_factura'      => isset( $invoice->tipo_factura ) ? $invoice->tipo_factura : 'F1',
				'aeat_final_status' => isset( $invoice->aeat_final_status ) ? $invoice->aeat_final_status : 'pending',
				'subsanation_count' => isset( $invoice->subsanation_count ) ? (int) $invoice->subsanation_count : 0,
				'error_category'    => isset( $invoice->error_category ) ? $invoice->error_category : '',
			), $invoice_data );
		}

		// Ensure required fields have defaults
		$invoice_data = array_merge( array(
			'invoice_id'        => 0,
			'order_id'          => 0,
			'invoice_number'    => '',
			'tipo_factura'      => 'F1',
			'aeat_final_status' => 'pending',
			'subsanation_count' => 0,
			'error_category'    => '',
		), $invoice_data );

		return $invoice_data;
	}

	/**
	 * Update invoice state in database.
	 *
	 * @param array         $invoice_data Invoice context.
	 * @param Freefactu_Aeat_Response $response     AEAT response.
	 * @param array         $decision     Decision result.
	 */
	private function update_invoice_state( array $invoice_data, Freefactu_Aeat_Response $response, array $decision ) {
		global $wpdb;
		$table_name = Freefactu_DB::get_table_name();

		// Determine new AEAT status based on response
		$new_aeat_status = 'pending';
		if ( $response->is_success() ) {
			$new_aeat_status = 'accepted';
		} elseif ( $response->get_status() === Freefactu_Aeat_Response::STATUS_ACCEPTED_WITH_ERRORS ) {
			$new_aeat_status = 'accepted_with_errors';
		} else {
			$new_aeat_status = 'rejected';
		}

		// Increment subsanation count if this was a subsanation attempt
		$new_subsanation_count = $invoice_data['subsanation_count'];
		if ( $decision['next_action'] === Freefactu_Aeat_Next_Action::RETRY_SUBSANATION ) {
			$new_subsanation_count++;
		}

		// If success after previous rejection, mark as subsanated
		if ( $response->is_success() && $invoice_data['aeat_final_status'] === 'rejected' ) {
			$new_aeat_status = 'subsanated';
		}

		// Build update data
		$update_data = array(
			'aeat_final_status' => $new_aeat_status,
			'error_category'    => $response->get_error_category(),
			'subsanation_count' => $new_subsanation_count,
			'error_message'     => $response->get_error_description(),
			'aeat_xml_response' => $response->get_raw_xml(),
		);

		// On success, also store CSV
		if ( $response->is_success() ) {
			$update_data['csv'] = $response->get_csv();
			$update_data['status'] = 'success';
			$update_data['error_message'] = '';
		} else {
			// Map to existing status field for compatibility
			$update_data['status'] = 'alert_aeat';
		}

		// Update based on what we have
		if ( ! empty( $invoice_data['invoice_id'] ) ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->update(
				$table_name,
				$update_data,
				array( 'id' => $invoice_data['invoice_id'] ),
				array( '%s', '%s', '%d', '%s', '%s', '%s', '%s' ),
				array( '%d' )
			);
		} elseif ( ! empty( $invoice_data['order_id'] ) ) {
			// For new invoices or when we only have order_id
			// Check if record exists first
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$exists = $wpdb->get_var( $wpdb->prepare(
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
				"SELECT id FROM " . esc_sql( $table_name ) . " WHERE order_id = %d LIMIT 1",
				$invoice_data['order_id']
			) );

			if ( $exists ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				$wpdb->update(
					$table_name,
					$update_data,
					array( 'order_id' => $invoice_data['order_id'] ),
					array( '%s', '%s', '%d', '%s', '%s', '%s', '%s' ),
					array( '%d' )
				);
			}
		}
	}

	/**
	 * Get the current subsanation status for an invoice.
	 *
	 * @param int $invoice_id Invoice ID.
	 * @return array|null Status array or null if not found.
	 */
	public function get_subsanation_status( $invoice_id ) {
		$invoice = Freefactu_DB::get_invoice_by_id( $invoice_id );

		if ( ! $invoice ) {
			return null;
		}

		$tipo_factura = isset( $invoice->tipo_factura ) ? $invoice->tipo_factura : 'F1';
		$current_status = isset( $invoice->aeat_final_status ) ? $invoice->aeat_final_status : 'pending';
		$error_category = isset( $invoice->error_category ) ? $invoice->error_category : '';
		$subsanation_count = isset( $invoice->subsanation_count ) ? $invoice->subsanation_count : 0;

		$can_subsanar = Freefactu_Aeat_Next_Action_Decider::can_subsanar(
			$tipo_factura,
			$current_status,
			$error_category
		);

		$max_attempts = apply_filters(
			'freefactu_max_subsanation_attempts',
			Freefactu_Aeat_Next_Action_Decider::DEFAULT_MAX_SUBSANATION_ATTEMPTS,
			array( 'invoice_id' => $invoice_id )
		);

		return array(
			'invoice_id'        => $invoice_id,
			'aeat_final_status' => $current_status,
			'error_category'    => $error_category,
			'error_message'     => $invoice->error_message,
			'subsanation_count' => $subsanation_count,
			'max_attempts'      => $max_attempts,
			'attempts_remaining'=> max( 0, $max_attempts - $subsanation_count ),
			'can_subsanar'      => $can_subsanar,
			'no_subsanation_reason' => $can_subsanar ? null : Freefactu_Aeat_Next_Action_Decider::explain_no_subsanation(
				$tipo_factura,
				$current_status,
				$error_category
			),
		);
	}

	/**
	 * Reset subsanation count for an invoice.
	 * Used when manually resetting or after successful subsanation.
	 *
	 * @param int $invoice_id Invoice ID.
	 * @return bool Success.
	 */
	public function reset_subsanation_count( $invoice_id ) {
		global $wpdb;
		$table_name = Freefactu_DB::get_table_name();

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$result = $wpdb->update(
			$table_name,
			array( 'subsanation_count' => 0 ),
			array( 'id' => $invoice_id ),
			array( '%d' ),
			array( '%d' )
		);

		return $result !== false;
	}

	/**
	 * Log the decision for debugging.
	 *
	 * @param array         $invoice_data Invoice context.
	 * @param Freefactu_Aeat_Response $response     AEAT response.
	 * @param array         $decision     Decision result.
	 */
	private function log_decision( array $invoice_data, Freefactu_Aeat_Response $response, array $decision ) {
		$log_message = sprintf(
			'AEAT Response for invoice #%s (order #%d): status=%s, category=%s, action=%s',
			$invoice_data['invoice_number'],
			$invoice_data['order_id'],
			$response->get_status(),
			$response->get_error_category(),
			$decision['next_action']
		);

		if ( $response->is_success() ) {
			Freefactu_Logger::log( $log_message, 'info' );
		} else {
			Freefactu_Logger::log( $log_message . ' - Error: ' . $response->get_error_description(), 'warning' );
		}
	}
}
