<?php
/**
 * AEAT Response
 *
 * Value object representing a normalized AEAT response.
 * Provides a consistent interface for handling AEAT responses regardless of
 * the underlying library implementation.
 *
 * @package Freefactu
 * @since 1.1.0
 */

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

/**
 * Class Freefactu_Aeat_Response
 *
 * Immutable value object representing a normalized AEAT response.
 * Contains all relevant data from an AEAT submission response.
 */
class Freefactu_Aeat_Response {

	/**
	 * Response status constants matching AEAT response statuses.
	 */
	const STATUS_SUCCESS              = 'success';
	const STATUS_REJECTED             = 'rejected';
	const STATUS_ACCEPTED_WITH_ERRORS = 'accepted_with_errors';
	const STATUS_PARTIAL              = 'partial';

	/**
	 * Normalized status.
	 *
	 * @var string One of STATUS_* constants.
	 */
	private $status;

	/**
	 * AEAT CSV code (only set on success).
	 *
	 * @var string
	 */
	private $csv;

	/**
	 * Raw AEAT error code.
	 *
	 * @var string
	 */
	private $error_code;

	/**
	 * Original error description from AEAT.
	 *
	 * @var string
	 */
	private $error_description;

	/**
	 * Classified error category.
	 *
	 * @var string One of Freefactu_Aeat_Error_Categories constants.
	 */
	private $error_category;

	/**
	 * Raw XML response from AEAT.
	 *
	 * @var string
	 */
	private $raw_xml;

	/**
	 * Individual record responses (for batch submissions).
	 *
	 * @var array
	 */
	private $items;

	/**
	 * Timestamp when response was received.
	 *
	 * @var string
	 */
	private $received_at;

	/**
	 * Constructor.
	 *
	 * @param array $data Response data array.
	 */
	public function __construct( array $data = array() ) {
		$this->status            = isset( $data['status'] ) ? $data['status'] : self::STATUS_REJECTED;
		$this->csv               = isset( $data['csv'] ) ? $data['csv'] : '';
		$this->error_code        = isset( $data['error_code'] ) ? $data['error_code'] : '';
		$this->error_description = isset( $data['error_description'] ) ? $data['error_description'] : '';
		$this->error_category    = isset( $data['error_category'] ) ? $data['error_category'] : Freefactu_Aeat_Error_Categories::UNKNOWN;
		$this->raw_xml           = isset( $data['raw_xml'] ) ? $data['raw_xml'] : '';
		$this->items             = isset( $data['items'] ) ? $data['items'] : array();
		$this->received_at       = isset( $data['received_at'] ) ? $data['received_at'] : current_time( 'mysql' );
	}

	/**
	 * Create from josemmo/verifactu-php library response.
	 *
	 * @param object $library_response Response from AeatClient::send().
	 * @param string $raw_xml          Raw XML response string.
	 * @return self
	 */
	public static function from_library_response( $library_response, $raw_xml = '' ) {
		$status = self::STATUS_REJECTED;
		$csv = '';
		$error_code = '';
		$error_description = '';
		$items = array();

		// Map library status to our constants
		if ( isset( $library_response->status ) ) {
			$lib_status = $library_response->status;
			
			// Check for josemmo\Verifactu\Models\Responses\ResponseStatus enum
			if ( $lib_status === \Freefactu_Vendor\josemmo\Verifactu\Models\Responses\ResponseStatus::Correct ) {
				$status = self::STATUS_SUCCESS;
			} elseif ( $lib_status === \Freefactu_Vendor\josemmo\Verifactu\Models\Responses\ResponseStatus::PartiallyCorrect ) {
				$status = self::STATUS_ACCEPTED_WITH_ERRORS;
			} elseif ( $lib_status === \Freefactu_Vendor\josemmo\Verifactu\Models\Responses\ResponseStatus::Incorrect ) {
				$status = self::STATUS_REJECTED;
			}
		}

		// Get CSV on success
		if ( $status === self::STATUS_SUCCESS && isset( $library_response->csv ) ) {
			$csv = $library_response->csv;
		}

		// Get error details from first item (typical single-invoice scenario)
		if ( isset( $library_response->items ) && ! empty( $library_response->items ) ) {
			$first_item = $library_response->items[0];
			
			if ( isset( $first_item->errorCode ) ) {
				$error_code = $first_item->errorCode;
			}
			if ( isset( $first_item->errorDescription ) ) {
				$error_description = $first_item->errorDescription;
			}

			// Convert all items to arrays for storage
			foreach ( $library_response->items as $item ) {
				$items[] = array(
					'status'           => isset( $item->status ) ? (string) $item->status : '',
					'error_code'       => isset( $item->errorCode ) ? $item->errorCode : '',
					'error_description'=> isset( $item->errorDescription ) ? $item->errorDescription : '',
					'csv'              => isset( $item->csv ) ? $item->csv : '',
				);
			}
		}

		return new self( array(
			'status'            => $status,
			'csv'               => $csv,
			'error_code'        => $error_code,
			'error_description' => $error_description,
			'raw_xml'           => $raw_xml,
			'items'             => $items,
		) );
	}

	/**
	 * Create a success response.
	 *
	 * @param string $csv     AEAT CSV code.
	 * @param string $raw_xml Raw XML response.
	 * @return self
	 */
	public static function success( $csv, $raw_xml = '' ) {
		return new self( array(
			'status'  => self::STATUS_SUCCESS,
			'csv'     => $csv,
			'raw_xml' => $raw_xml,
		) );
	}

	/**
	 * Create a rejected response.
	 *
	 * @param string $error_code        AEAT error code.
	 * @param string $error_description Error description.
	 * @param string $raw_xml           Raw XML response.
	 * @return self
	 */
	public static function rejected( $error_code, $error_description, $raw_xml = '' ) {
		return new self( array(
			'status'            => self::STATUS_REJECTED,
			'error_code'        => $error_code,
			'error_description' => $error_description,
			'raw_xml'           => $raw_xml,
		) );
	}

	// ===== Getters (immutable object) =====

	public function get_status() {
		return $this->status;
	}

	public function get_csv() {
		return $this->csv;
	}

	public function get_error_code() {
		return $this->error_code;
	}

	public function get_error_description() {
		return $this->error_description;
	}

	public function get_error_category() {
		return $this->error_category;
	}

	public function get_raw_xml() {
		return $this->raw_xml;
	}

	public function get_items() {
		return $this->items;
	}

	public function get_received_at() {
		return $this->received_at;
	}

	// ===== Helper methods =====

	/**
	 * Check if response indicates success.
	 *
	 * @return bool
	 */
	public function is_success() {
		return $this->status === self::STATUS_SUCCESS;
	}

	/**
	 * Check if response indicates rejection.
	 *
	 * @return bool
	 */
	public function is_rejected() {
		return $this->status === self::STATUS_REJECTED;
	}

	/**
	 * Check if response has errors (rejected or accepted with errors).
	 *
	 * @return bool
	 */
	public function has_errors() {
		return $this->status !== self::STATUS_SUCCESS || ! empty( $this->error_code );
	}

	/**
	 * Set the error category (called by classifier).
	 *
	 * @param string $category Error category.
	 * @return self New instance with category set.
	 */
	public function with_category( $category ) {
		$data = array(
			'status'            => $this->status,
			'csv'               => $this->csv,
			'error_code'        => $this->error_code,
			'error_description' => $this->error_description,
			'error_category'    => $category,
			'raw_xml'           => $this->raw_xml,
			'items'             => $this->items,
			'received_at'       => $this->received_at,
		);
		return new self( $data );
	}

	/**
	 * Convert to array for storage.
	 *
	 * @return array
	 */
	public function to_array() {
		return array(
			'status'            => $this->status,
			'csv'               => $this->csv,
			'error_code'        => $this->error_code,
			'error_description' => $this->error_description,
			'error_category'    => $this->error_category,
			'raw_xml'           => $this->raw_xml,
			'items'             => $this->items,
			'received_at'       => $this->received_at,
		);
	}
}
