<?php
/**
 * Freefactu Declaration Manager
 *
 * Manages the creation, storage, and retrieval of Declaraciones Responsables.
 * Handles PDF generation and SIF ZIP archive creation.
 *
 * @package FreeFacTu
 * @since   1.1.0
 */

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

/**
 * Class Freefactu_Declaration_Manager
 *
 * Handles all operations related to responsible declarations for the SIF.
 */
class Freefactu_Declaration_Manager {

	/**
	 * Get the declarations storage directory path.
	 *
	 * @return string Absolute path to declarations directory.
	 */
	public static function get_declarations_dir() {
		$upload_dir = wp_upload_dir();
		return $upload_dir['basedir'] . '/freefactu-declarations';
	}

	/**
	 * Ensure the declarations directory exists.
	 *
	 * @return bool True if directory exists or was created successfully.
	 */
	public static function ensure_declarations_dir() {
		$dir = self::get_declarations_dir();
		if ( ! Freefactu_Filesystem::exists( $dir ) ) {
			return Freefactu_Filesystem::mkdir( $dir );
		}
		return true;
	}

	/**
	 * Get Spanish month name.
	 *
	 * @param int $month Month number (1-12).
	 * @return string Spanish month name.
	 */
	private static function get_spanish_month( $month ) {
		$months = array(
			1  => 'enero',
			2  => 'febrero',
			3  => 'marzo',
			4  => 'abril',
			5  => 'mayo',
			6  => 'junio',
			7  => 'julio',
			8  => 'agosto',
			9  => 'septiembre',
			10 => 'octubre',
			11 => 'noviembre',
			12 => 'diciembre',
		);
		return $months[ (int) $month ] ?? '';
	}

	/**
	 * Format date in Spanish.
	 *
	 * @param string $format Format string ('long' or 'short').
	 * @return string Formatted date in Spanish.
	 */
	private static function format_spanish_date( $format = 'long' ) {
		$day   = current_time( 'd' );
		$month = self::get_spanish_month( current_time( 'n' ) );
		$year  = current_time( 'Y' );

		if ( 'short' === $format ) {
			return current_time( 'd/m/Y' );
		}

		return "{$day} de {$month} de {$year}";
	}

	/**
	 * Generate the complete declaration content from template.
	 *
	 * This follows the official AEAT format with numbered fields (1.a-1.l and ANEXO).
	 *
	 * @param array $data Declaration data.
	 * @return string Complete declaration content in Markdown.
	 */
	public static function generate_declaration_content( $data ) {
		// Extract data with defaults
		$operator_name        = $data['operator_name'] ?? '';
		$operator_nif         = $data['operator_nif'] ?? '';
		$software_name        = $data['software_name'] ?? '';
		$software_version     = $data['software_version'] ?? FREEFACTU_VERSION;
		$software_id_code     = $data['software_id_code'] ?? 'SIF1';
		$installation_number  = $data['installation_number'] ?? '001';
		$producer_name        = $data['producer_name'] ?? $operator_name;
		$producer_nif         = $data['producer_nif'] ?? $operator_nif;
		$producer_address     = $data['producer_address'] ?? '';
		$producer_postal_code = $data['producer_postal_code'] ?? '';
		$producer_city        = $data['producer_city'] ?? '';
		$producer_phone       = $data['producer_phone'] ?? '';
		$producer_email       = $data['producer_email'] ?? '';
		$producer_website     = $data['producer_website'] ?? '';
		$declaration_place    = $data['declaration_place'] ?? $producer_city;
		$is_verifactu_only    = $data['is_verifactu_only'] ?? 'S';
		$is_multi_user        = $data['is_multi_user'] ?? 'N';
		
		// Use Spanish date formatting
		$date = self::format_spanish_date( 'long' );
		$date_simple = self::format_spanish_date( 'short' );

		ob_start();
		?>
# DECLARACIÓN RESPONSABLE
# DEL SISTEMA INFORMÁTICO DE FACTURACIÓN

## DATOS DEL PRODUCTOR Y DEL SISTEMA INFORMÁTICO


#### Nombre del sistema informático a que se refiere esta declaración responsable:

<?php echo esc_html( $software_name ); ?>

#### Código identificador del sistema informático a que se refiere el apartado a) de esta declaración responsable:

<?php echo esc_html( $software_id_code ); ?>

#### Identificador completo de la versión concreta del sistema informático a que se refiere esta declaración responsable:

<?php echo esc_html( $software_version ); ?>

### Componentes de software, de que consta el sistema informático a que se refiere esta declaración responsable, junto con una breve descripción de lo que hace dicho sistema informático y de sus principales funcionalidades:**

#### WordPress:

Es un sistema informático de código abierto, distribuido bajo licencia GNU General Public License (GPL), versión 2 o posterior, que permite la creación, configuración y gestión de sitios web y aplicaciones. Funciona sobre una arquitectura web basada en PHP y bases de datos relacionales, siendo compatible con distintos entornos de alojamiento y servidores web. Proporciona un sistema extensible mediante la instalación de plugins y temas, que permiten ampliar o modificar sus funcionalidades sin alterar el núcleo del sistema.

#### WooCommerce:

Es un plugin de código abierto para WordPress, distribuido bajo licencia GNU General Public License (GPL), versión 2 o posterior, que permite la creación y gestión de tiendas electrónicas en línea. Proporciona funcionalidades para la gestión de productos, clientes, pedidos y pagos, integrándose con diferentes métodos de pago y envío. Permite la generación y administración de documentos comerciales asociados a los pedidos, como pedidos y facturas, mediante su configuración o mediante extensiones adicionales. Su funcionamiento depende del entorno WordPress en el que se instala.

#### FreeFactu:

Es un conjunto de herramientas de desarrollo de código abierto, en forma de plugin de WordPress, distribuido bajo licencia GNU General Public License (GPL), versión 2 o posterior, que facilita la creación, configuración e implementación de sistemas informáticos de facturación conforme a las características técnicas del sistema VERI*FACTU, integrándose con WooCommerce para comercios electrónicos, no constituyendo por sí mismo un sistema de facturación certificado ni autónomo, siendo el operador el único responsable de su uso, configuración y cumplimiento de la normativa fiscal aplicable.

**Este conjunto está pensado para ser instalado en servidores web propios o contratados por el usuario (on-premise o hosting), siendo compatible con cualquier servidor que cumpla los siguientes requisitos técnicos mínimos:**
- PHP 8.2 o superior
- Extensión PHP OpenSSL habilitada
- Base de datos MySQL 5.7 o superior
- Extensión PHP SOAP habilitada
- Memoria PHP mínima de 256 MB
- Conexión HTTPS con certificado SSL válido

**Este SIP cuenta con las siguientes funcionalidades principales:**
- Capturar información de facturación desde pedidos de WooCommerce
- Expedir facturas con formato VERI*FACTU conforme al RD 1007/2023
- Generación automática de registros de facturación con huella digital SHA-256
- Encadenamiento criptográfico de registros de facturación
- Firma electrónica de los envíos mediante certificado digital cualificado del obligado tributario
- Envío inmediato de registros a la Agencia Tributaria
- Generación de código QR tributario para verificación
- Generación de facturas en formato PDF
- Consulta y gestión del histórico de facturas emitidas
- Estadísticas de facturación
- Exportación y conservación de registros durante el periodo legalmente exigido (mínimo 4 años, recomendado 6 años)

#### Indicación de si el sistema informático a que se refiere esta declaración responsable se ha producido de tal manera que, a los efectos de cumplir con el Reglamento, solo pueda funcionar exclusivamente como «VERI*FACTU»:

<?php echo esc_html( $is_verifactu_only ); ?> - Sí

#### Indicación de si el sistema informático a que se refiere la declaración responsable permite ser usado por varios obligados tributarios o por un mismo usuario para dar soporte a la facturación de varios obligados tributarios:

N - No. Este sistema está diseñado para una única instalación de WooCommerce vinculada a un único obligado tributario.

#### Tipos de firma utilizados para firmar los registros de facturación y de evento en el caso de que el sistema informático a que se refiere esta declaración responsable no sea utilizado como «VERI*FACTU»:

Dado que se trata de un producto de facturación que solo puede ser utilizado exclusivamente en la modalidad de «VERI*FACTU», según la interpretación técnica publicada por la AEAT, no es necesario firmar electrónicamente los registros de facturación XML de forma individual (firma XAdES), ya que quedan debidamente autenticados al ser remitidos a los servicios electrónicos de la Agencia Tributaria mediante el certificado electrónico cualificado del obligado tributario utilizado en la comunicación HTTPS.

#### Razón social de la entidad productora del sistema informático a que se refiere esta declaración responsable:

<?php echo esc_html( $producer_name ); ?>

#### Número de identificación fiscal (NIF) español de la entidad productora del sistema informático a que se refiere esta declaración responsable:

<?php echo esc_html( $producer_nif ); ?>

#### Dirección postal completa de contacto de la entidad productora del sistema informático a que se refiere esta declaración responsable:

<?php echo esc_html( $producer_address ); ?>
<?php echo esc_html( $producer_postal_code ); ?> – <?php echo esc_html( $producer_city ); ?>
España.

La entidad productora del sistema informático a que se refiere esta declaración responsable hace constar que dicho sistema informático, en la versión indicada en ella, cumple con lo dispuesto en el artículo 29.2.j) de la Ley 58/2003, de 17 de diciembre, General Tributaria, en el Reglamento que establece los requisitos que deben adoptar los sistemas y programas informáticos o electrónicos que soporten los procesos de facturación de empresarios y profesionales, y la estandarización de formatos de los registros de facturación, aprobado por el Real Decreto 1007/2023, de 5 de diciembre, en la Orden HAC/1177/2024, de 17 de octubre, y en la sede electrónica de la Agencia Estatal de Administración Tributaria para todo aquello que complete las especificaciones de dicha orden.

#### Fecha y lugar en que la entidad productora de este sistema informático suscribe esta declaración responsable del mismo:

- Fecha: <?php echo esc_html( $date ); ?>
- Lugar: <?php echo esc_html( $declaration_place ); ?> – España.

#### Otras formas de contacto con la entidad productora del sistema informático a que se refiere esta declaración responsable:

- Teléfono: <?php echo esc_html( $producer_phone ); ?>
- Correo electrónico: <?php echo esc_html( $producer_email ); ?>

#### Direcciones de internet de la entidad productora del sistema informático a que se refiere esta declaración responsable:

- Sitio web de la empresa: <?php echo esc_html( $producer_website ); ?>
- Sede electrónica de la AEAT (VERI*FACTU): https://sede.agenciatributaria.gob.es/

#### El sistema informático a que se refiere esta declaración responsable cumple las diferentes especificaciones técnicas y funcionales contenidas en la Orden HAC/1177/2024, de 17 de octubre, y en la sede electrónica de la Agencia Estatal de Administración Tributaria para todo aquello que complete las especificaciones de dicha orden, de la siguiente manera:

Además del modo que es de obligado cumplimiento en ciertos casos (como el algoritmo de huella a emplear), otras implementaciones utilizadas son:

- Empleo de huella digital SHA-256 para garantizar la integridad de los registros de facturación.
- Encadenamiento de facturas mediante inclusión del hash de la factura anterior en cada nuevo registro.
- Autenticación de los envíos mediante certificado electrónico cualificado del obligado tributario (según interpretación técnica de la AEAT, no se requiere firma XAdES individual de los registros XML en modalidad VERI*FACTU).
- Transmisión en tiempo real a los servicios web de la Agencia Tributaria mediante conexión HTTPS segura.
- Generación automática de código QR tributario en cada factura emitida.
- Consolidación de la generación del registro de facturación en el momento de expedición de la factura.
- Conservación de registros de facturación durante el periodo legalmente exigido (mínimo 4 años, recomendado 6 años), con posibilidad de exportación en formatos CSV y XML.

### Medidas de seguridad implementadas:

El sistema implementa las siguientes medidas de seguridad para garantizar la integridad y confidencialidad de los datos:

- Verificación del certificado digital del servidor de la AEAT en cada comunicación.
- Almacenamiento seguro del certificado digital del obligado tributario con contraseña cifrada.
- Protección de los archivos de certificados mediante restricción de acceso.
- Registro de auditoría de todas las operaciones de facturación realizadas.
- Generación de snapshots inmutables de las facturas en el momento del envío a la AEAT.
- Validación de integridad mediante verificación del encadenamiento de hashes.

## DECLARACIÓN DE RESPONSABILIDAD DEL OPERADOR

El operador identificado a continuación (usuario del sistema) RECONOCE y ACEPTA que:

| Campo | Valor |
|-------|-------|
| **Razón Social / Nombre** | <?php echo esc_html( $operator_name ); ?> |
| **NIF/CIF** | <?php echo esc_html( $operator_nif ); ?> |
| **Número de Instalación** | <?php echo esc_html( $installation_number ); ?> |

### Responsabilidad del Operador

1. **ES EL ÚNICO RESPONSABLE** del uso, configuración y operación del Sistema Informático de Facturación.

2. **ASUME TODA RESPONSABILIDAD** sobre:
   - La veracidad de los datos introducidos en el sistema
   - El cumplimiento de las obligaciones fiscales derivadas de la facturación
   - La correcta configuración del certificado digital y credenciales
   - La integridad de los registros de facturación generados
   - Las consecuencias fiscales y legales del uso del sistema

3. **DECLARA CONOCER** que:
   - Las facturas enviadas a la AEAT en modalidad VERI*FACTU tienen carácter definitivo
   - Los errores deben corregirse mediante facturas rectificativas
   - Debe conservar copia de toda la documentación fiscal durante un mínimo de 4 años (recomendado 6 años) según la normativa vigente

## Naturaleza del Software

### El operador RECONOCE y ACEPTA que:

1. **Freefactu Toolkit es únicamente un conjunto de herramientas (toolkit)** de software que facilita la generación y envío de facturas electrónicas conforme al sistema VERI*FACTU.

2. **Freefactu Toolkit NO ASUME RESPONSABILIDAD ALGUNA** sobre:
   - El cumplimiento fiscal del operador
   - La veracidad de los datos fiscales introducidos
   - Las consecuencias legales o económicas del uso del sistema
   - Los errores derivados de una configuración incorrecta
   - La interpretación de la normativa fiscal aplicable

3. **El software se proporciona `TAL CUAL`** (AS IS), sin garantías expresas o implícitas sobre su idoneidad para un propósito particular.

## REFERENCIAS LEGALES

Esta declaración se emite en cumplimiento de:

- **Ley 58/2003, art. 29.2.j)** - Ley General Tributaria
- **Real Decreto 1007/2023, de 5 de diciembre** - Requisitos de los sistemas informáticos de facturación
- **Orden HAC/1177/2024, de 17 de octubre** - Especificaciones técnicas del sistema VERI*FACTU
- **Ley 11/2021** - Medidas de prevención y lucha contra el fraude fiscal

## FIRMA Y CONFORMIDAD

El operador identificado en este documento declara que toda la información proporcionada es veraz y acepta los términos y condiciones establecidos en esta declaración.

**Operador:** <?php echo esc_html( $operator_name ); ?>
**NIF/CIF:** <?php echo esc_html( $operator_nif ); ?>
**Fecha:** <?php echo esc_html( $date_simple ); ?>

*Declaración generada por <?php echo esc_html( $software_name ); ?> v<?php echo esc_html( $software_version ); ?>*
*Instalación Nº: <?php echo esc_html( $installation_number ); ?>*
*Productor: <?php echo esc_html( $producer_name ); ?> (<?php echo esc_html( $producer_nif ); ?>)*

<?php
		$content = ob_get_clean();

		return $content;
	}

	/**
	 * Generate PDF from declaration content.
	 *
	 * @param string $content   Markdown content of the declaration.
	 * @param array  $metadata  Declaration metadata.
	 * @return string|WP_Error  PDF content or WP_Error on failure.
	 */
	public static function generate_declaration_pdf( $content, $metadata = array() ) {
		// Check if DOMPDF is available using the vendor loader
		if ( ! Freefactu_Vendor_Loader::dompdf_available() ) {
			return new WP_Error(
				'dompdf_not_found',
				__( 'DOMPDF no está disponible. Por favor, reinstale el plugin.', 'freefactu' )
			);
		}

		try {
			// Convert Markdown to HTML
			$html_content = self::markdown_to_html( $content );

			// Build complete HTML document
			$html = self::build_pdf_html( $html_content, $metadata );

			// Create DOMPDF instance using vendor loader
			$options = Freefactu_Vendor_Loader::dompdf_options();
			$options->set( 'isRemoteEnabled', true );
			$options->set( 'isHtml5ParserEnabled', true );
			$options->set( 'defaultFont', 'DejaVu Sans' );
			$options->set( 'tempDir', sys_get_temp_dir() );

			$dompdf = Freefactu_Vendor_Loader::dompdf( $options );
			$dompdf->loadHtml( $html );
			$dompdf->setPaper( 'A4', 'portrait' );
			$dompdf->render();

			return $dompdf->output();

		} catch ( Exception $e ) {
			Freefactu_Logger::log( 'Declaration PDF Generation Error: ' . $e->getMessage(), 'error' );
			return new WP_Error( 'pdf_generation_failed', $e->getMessage() );
		}
	}

	/**
	 * Convert Markdown to HTML.
	 *
	 * @param string $markdown Markdown content.
	 * @return string HTML content.
	 */
	private static function markdown_to_html( $markdown ) {
		// Simple markdown conversion (headers, bold, lists, tables)
		$html = $markdown;

		// Headers
		$html = preg_replace( '/^# (.+)$/m', '<h1>$1</h1>', $html );
		$html = preg_replace( '/^## (.+)$/m', '<h2>$1</h2>', $html );
		$html = preg_replace( '/^### (.+)$/m', '<h3>$1</h3>', $html );
		$html = preg_replace( '/^#### (.+)$/m', '<h4>$1</h4>', $html );

		// Bold
		$html = preg_replace( '/\*\*(.+?)\*\*/', '<strong>$1</strong>', $html );

		// Horizontal rules
		$html = preg_replace( '/^---$/m', '<hr>', $html );

		// Code blocks
		$html = preg_replace( '/`([^`]+)`/', '<code>$1</code>', $html );

		// Lists
		$html = preg_replace( '/^- (.+)$/m', '<li>$1</li>', $html );
		$html = preg_replace( '/^(\d+)\. (.+)$/m', '<li>$2</li>', $html );
		$html = preg_replace( '/(<li>.+<\/li>\n?)+/', '<ul>$0</ul>', $html );
		
		// Tables (simple conversion)
		$lines = explode( "\n", $html );
		$in_table = false;
		$table_html = '';
		$result_lines = array();

		foreach ( $lines as $line ) {
			if ( preg_match( '/^\|(.+)\|$/', trim( $line ) ) ) {
				if ( ! $in_table ) {
					$in_table = true;
					$table_html = '<table class="declaration-table"><tbody>';
				}
				
				// Skip separator rows
				if ( preg_match( '/^\|[-\s|]+\|$/', trim( $line ) ) ) {
					continue;
				}

				$cells = array_map( 'trim', explode( '|', trim( $line, '|' ) ) );
				$table_html .= '<tr>';
				foreach ( $cells as $cell ) {
					$tag = strpos( $cell, '**' ) !== false ? 'th' : 'td';
					$cell = strip_tags( $cell, '<strong><code><em>' );
					$table_html .= "<{$tag}>{$cell}</{$tag}>";
				}
				$table_html .= '</tr>';
			} else {
				if ( $in_table ) {
					$table_html .= '</tbody></table>';
					$result_lines[] = $table_html;
					$table_html = '';
					$in_table = false;
				}
				$result_lines[] = $line;
			}
		}

		if ( $in_table ) {
			$table_html .= '</tbody></table>';
			$result_lines[] = $table_html;
		}

		$html = implode( "\n", $result_lines );

		// Paragraphs (wrap remaining text)
		$html = preg_replace( '/^([^<\n].+)$/m', '<p>$1</p>', $html );

		// Add class to paragraphs containing numbered field labels (1.a), 2.b), etc.)
		$html = preg_replace( 
			'/<p><strong>(\d+\.[a-z]\))/',
			'<p class="field-paragraph"><strong>$1',
			$html
		);

		// Clean up empty paragraphs
		$html = preg_replace( '/<p>\s*<\/p>/', '', $html );

		return $html;
	}

	/**
	 * Build the complete HTML document for PDF generation.
	 *
	 * IMPORTANT: This method generates HTML with inline <style> tags intentionally.
	 * This is NOT a WordPress page - it generates standalone HTML for DOMPDF
	 * to convert into a PDF document. DOMPDF requires inline CSS because:
	 * 1. External stylesheets cannot be reliably loaded during PDF generation
	 * 2. wp_enqueue_style() is not available in this context (no WordPress page rendering)
	 * 3. The HTML is never displayed in a browser - only converted to PDF
	 *
	 * @see https://github.com/dompdf/dompdf/wiki/CSSCompatibility
	 *
	 * @param string $content  HTML content.
	 * @param array  $metadata Metadata.
	 * @return string Complete HTML document.
	 */
	private static function build_pdf_html( $content, $metadata = array() ) {
		$date = current_time( 'd/m/Y H:i' );
		// Use the declaration's software version, not the plugin version
		$version = isset( $metadata['software_version'] ) ? $metadata['software_version'] : FREEFACTU_VERSION;
		$software_name = isset( $metadata['software_name'] ) ? $metadata['software_name'] : 'Freefactu Toolkit';
		
		ob_start();
		?>
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Declaración Responsable SIF</title>
    <?php // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- DOMPDF PDF template: wp_enqueue_style() is unavailable in this context. See method docblock. ?>
    <style>
        @page {
            /* 25% more margin on sides: 18.75mm * 1.25 = 23.4mm */
            margin: 20mm 23.4mm 25mm 23.4mm;
        }
        body {
            font-family: 'DejaVu Sans', sans-serif;
            font-size: 8pt;
            line-height: 1.4;
            color: #333;
        }
        /* Main title: larger, bold, no underline, less line spacing */
        h1 {
            font-size: 14pt;
            font-weight: bold;
            color: #1a365d;
            margin: 0 0 10px 0;
            padding: 0;
            text-transform: uppercase;
            letter-spacing: 0.5px;
            line-height: 1.1;
            page-break-after: avoid;
        }
        /* Consecutive h1s (second line of title) */
        h1 + h1 {
            margin-top: -5px;
            margin-bottom: 15px;
        }
        /* Section titles (ANEXO, etc) - with top margin */
        h2 {
            font-size: 12pt;
            color: #2d3748;
            margin: 20px 0 8px 0;
            padding: 4px 8px;
            background: #edf2f7;
            border-left: 3px solid #2b6cb0;
            page-break-after: avoid;
            page-break-inside: avoid;
        }
        /* Subsection titles (Responsabilidad del Operador, etc) - with top margin */
        h3 {
            font-size: 10pt;
            color: #4a5568;
            margin: 15px 0 5px 0;
            font-weight: 600;
            page-break-after: avoid;
            page-break-inside: avoid;
        }
        h4 {
            font-size: 8pt;
            color: #4a5568;
            margin: 15px 0 5px 0;
            font-weight: 600;
            page-break-after: avoid;
            page-break-inside: avoid;
        }
        p {
            margin: 4px 0;
            text-align: justify;
        }
        /* Numbered field paragraphs (1.a), 2.b), etc.) - add top margin */
        p.field-paragraph {
            margin-top: 12px;
            page-break-inside: avoid;
        }
        ul, ol {
            margin: 4px 0;
            padding-left: 20px;
        }
        li {
            margin: 2px 0;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            margin: 8px 0;
            font-size: 7.5pt;
            page-break-inside: avoid;
        }
        table.declaration-table th,
        table.declaration-table td {
            border: 1px solid #cbd5e0;
            padding: 5px 8px;
            text-align: left;
        }
        table.declaration-table th {
            background: #edf2f7;
            font-weight: 600;
            color: #2d3748;
        }
        table.declaration-table tr:nth-child(even) {
            background: #f7fafc;
        }
        code {
            background: #edf2f7;
            padding: 1px 4px;
            border-radius: 2px;
            font-size: 7pt;
            font-family: 'DejaVu Sans Mono', monospace;
        }
        /* Hide horizontal rules (separators) */
        hr {
            display: none;
        }
        strong {
            color: #2d3748;
        }
        .footer-last-page {
            text-align: center;
            font-size: 7pt;
            color: #718096;
            border-top: 1px solid #e2e8f0;
            padding-top: 8px;
            margin-top: 20px;
        }
    </style>
</head>
<body>
    <?php echo wp_kses_post( $content ); ?>
    <div class="footer-last-page">
        Declaración generada el <?php echo esc_html( $date ); ?> · <?php echo esc_html( $software_name ); ?> v<?php echo esc_html( $version ); ?>
    </div>
</body>
</html>
		<?php
		return ob_get_clean();
	}

	/**
	 * Generate SIF ZIP archive.
	 *
	 * Creates a ZIP file containing:
	 * - Plugin source code
	 * - Uploads (PDFs, certificates)
	 * - Database export
	 *
	 * @param int   $declaration_id Declaration ID for naming.
	 * @param array $metadata       Additional metadata.
	 * @return string|WP_Error Path to ZIP file or WP_Error.
	 */
	public static function generate_sif_zip( $declaration_id, $metadata = array() ) {
		Freefactu_Logger::log( 'generate_sif_zip called for declaration: ' . $declaration_id, 'info' );
		
		self::ensure_declarations_dir();
		$declarations_dir = self::get_declarations_dir();
		$zip_filename = 'sif-' . $declaration_id . '-' . gmdate( 'Ymd-His' ) . '.zip';
		$zip_path = $declarations_dir . '/' . $zip_filename;
		
		Freefactu_Logger::log( 'Creating ZIP at: ' . $zip_path, 'info' );

		// Try ZipArchive first, fall back to PclZip
		$use_pclzip = ! class_exists( 'ZipArchive' );
		
		if ( $use_pclzip ) {
			Freefactu_Logger::log( 'ZipArchive not available, using PclZip fallback', 'info' );
			return self::generate_sif_zip_pclzip( $zip_path, $declaration_id, $metadata );
		}
		
		$zip = new ZipArchive();
		$result = $zip->open( $zip_path, ZipArchive::CREATE | ZipArchive::OVERWRITE );

		if ( $result !== true ) {
			Freefactu_Logger::log( 'Failed to open ZIP for writing. Error code: ' . $result, 'error' );
			// Try PclZip as fallback
			Freefactu_Logger::log( 'Falling back to PclZip...', 'info' );
			return self::generate_sif_zip_pclzip( $zip_path, $declaration_id, $metadata );
		}

		try {
			// 1. Add plugin directory
			$plugin_dir = FREEFACTU_PLUGIN_DIR;
			Freefactu_Logger::log( 'Adding plugin directory: ' . $plugin_dir, 'info' );
			self::add_directory_to_zip( $zip, $plugin_dir, 'plugins/freefactu' );

			// 2. Add uploads/freefactu
			$upload_dir = wp_upload_dir();
			$freefactu_uploads = $upload_dir['basedir'] . '/freefactu';
			if ( Freefactu_Filesystem::exists( $freefactu_uploads ) ) {
				Freefactu_Logger::log( 'Adding uploads directory: ' . $freefactu_uploads, 'info' );
				self::add_directory_to_zip( $zip, $freefactu_uploads, 'uploads/freefactu' );
			}

			// 3. Add uploads/freefactu-certs (if exists)
			$certs_dir = $upload_dir['basedir'] . '/freefactu-certs';
			if ( Freefactu_Filesystem::exists( $certs_dir ) ) {
				Freefactu_Logger::log( 'Adding certs directory: ' . $certs_dir, 'info' );
				self::add_directory_to_zip( $zip, $certs_dir, 'uploads/freefactu-certs' );
			}

			// 4. Add database export
			Freefactu_Logger::log( 'Generating database export...', 'info' );
			$db_export = self::export_database_tables();
			if ( ! is_wp_error( $db_export ) ) {
				$zip->addFromString( 'database_export.sql', $db_export );
				Freefactu_Logger::log( 'Database export added', 'info' );
			} else {
				Freefactu_Logger::log( 'Database export failed: ' . $db_export->get_error_message(), 'error' );
			}

			// 5. Add manifest file
			$manifest = self::generate_manifest( $metadata );
			$zip->addFromString( 'MANIFEST.txt', $manifest );
			Freefactu_Logger::log( 'Manifest added', 'info' );

			$close_result = $zip->close();
			Freefactu_Logger::log( 'ZIP close result: ' . ( $close_result ? 'success' : 'failed' ), 'info' );
			
			if ( ! $close_result ) {
				return new WP_Error( 'zip_close_failed', __( 'No se pudo cerrar el archivo ZIP.', 'freefactu' ) );
			}

			// Verify the file was created
			if ( ! file_exists( $zip_path ) ) {
				Freefactu_Logger::log( 'ZIP file does not exist after close!', 'error' );
				return new WP_Error( 'zip_file_missing', __( 'El archivo ZIP no se creó correctamente.', 'freefactu' ) );
			}
			
			$file_size = filesize( $zip_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_write
			Freefactu_Logger::log( 'ZIP file created successfully. Size: ' . $file_size . ' bytes', 'info' );

			return $zip_path;

		} catch ( Exception $e ) {
			Freefactu_Logger::log( 'ZIP generation exception: ' . $e->getMessage(), 'error' );
			$zip->close();
			if ( file_exists( $zip_path ) ) {
				wp_delete_file( $zip_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_write
			}
			return new WP_Error( 'zip_generation_failed', $e->getMessage() );
		}
	}

	/**
	 * Generate SIF ZIP using PclZip (WordPress fallback).
	 *
	 * @param string $zip_path       Full path for the ZIP file.
	 * @param int    $declaration_id Declaration ID.
	 * @param array  $metadata       Additional metadata.
	 * @return string|WP_Error Path to ZIP file or WP_Error.
	 */
	private static function generate_sif_zip_pclzip( $zip_path, $declaration_id, $metadata = array() ) {
		// WordPress core PclZip fallback — loaded per WordPress documentation and used
		// immediately below. This is the standard approach for ZIP operations on servers
		// that don't have the ZipArchive extension (see ZipArchive fallback above).
		// @see https://developer.wordpress.org/reference/classes/pclzip/
		require_once ABSPATH . 'wp-admin/includes/class-pclzip.php';
		
		$zip = new PclZip( $zip_path );
		$files_to_add = array();
		$temp_files = array();
		
		try {
			$upload_dir = wp_upload_dir();
			
			// 1. Add plugin directory
			$plugin_dir = FREEFACTU_PLUGIN_DIR;
			Freefactu_Logger::log( 'PclZip: Adding plugin directory: ' . $plugin_dir, 'info' );
			$files_to_add[] = array(
				PCLZIP_ATT_FILE_NAME => $plugin_dir,
				PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'plugins/freefactu',
			);
			
			// 2. Add uploads/freefactu
			$freefactu_uploads = $upload_dir['basedir'] . '/freefactu';
			if ( Freefactu_Filesystem::exists( $freefactu_uploads ) ) {
				Freefactu_Logger::log( 'PclZip: Adding uploads directory: ' . $freefactu_uploads, 'info' );
				$files_to_add[] = array(
					PCLZIP_ATT_FILE_NAME => $freefactu_uploads,
					PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'uploads/freefactu',
				);
			}
			
			// 3. Add uploads/freefactu-certs (if exists)
			$certs_dir = $upload_dir['basedir'] . '/freefactu-certs';
			if ( Freefactu_Filesystem::exists( $certs_dir ) ) {
				Freefactu_Logger::log( 'PclZip: Adding certs directory: ' . $certs_dir, 'info' );
				$files_to_add[] = array(
					PCLZIP_ATT_FILE_NAME => $certs_dir,
					PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'uploads/freefactu-certs',
				);
			}
			
			// Create the ZIP with directories
			if ( ! empty( $files_to_add ) ) {
				// Use simple approach - add plugin directory
				$result = $zip->create( $plugin_dir, PCLZIP_OPT_REMOVE_PATH, dirname( FREEFACTU_PLUGIN_DIR ), PCLZIP_OPT_ADD_PATH, 'plugins' );
				if ( $result === 0 ) {
					Freefactu_Logger::log( 'PclZip error: ' . $zip->errorInfo( true ), 'error' );
					return new WP_Error( 'pclzip_failed', $zip->errorInfo( true ) );
				}
				
				// Add uploads if exists
				if ( Freefactu_Filesystem::exists( $freefactu_uploads ) ) {
					$zip->add( $freefactu_uploads, PCLZIP_OPT_REMOVE_PATH, $upload_dir['basedir'], PCLZIP_OPT_ADD_PATH, 'uploads' );
				}
				if ( Freefactu_Filesystem::exists( $certs_dir ) ) {
					$zip->add( $certs_dir, PCLZIP_OPT_REMOVE_PATH, $upload_dir['basedir'], PCLZIP_OPT_ADD_PATH, 'uploads' );
				}
			}
			
			// 4. Add database export as temp file
			Freefactu_Logger::log( 'PclZip: Generating database export...', 'info' );
			$db_export = self::export_database_tables();
			if ( ! is_wp_error( $db_export ) ) {
				$temp_db_file = $upload_dir['basedir'] . '/freefactu-declarations/temp_db_export_' . $declaration_id . '.sql';
				Freefactu_Filesystem::put_contents( $temp_db_file, $db_export );
				$temp_files[] = $temp_db_file;
				$zip->add( $temp_db_file, PCLZIP_OPT_REMOVE_PATH, dirname( $temp_db_file ) );
			}
			
			// 5. Add manifest as temp file
			$manifest = self::generate_manifest( $metadata );
			$temp_manifest_file = $upload_dir['basedir'] . '/freefactu-declarations/temp_manifest_' . $declaration_id . '.txt';
			Freefactu_Filesystem::put_contents( $temp_manifest_file, $manifest );
			$temp_files[] = $temp_manifest_file;
			$zip->add( $temp_manifest_file, PCLZIP_OPT_REMOVE_PATH, dirname( $temp_manifest_file ) );
			
			// Rename manifest file in ZIP
			// Note: PclZip doesn't support renaming, so we'll use the temp filename
			
			// Clean up temp files
			foreach ( $temp_files as $temp_file ) {
				if ( file_exists( $temp_file ) ) {
					wp_delete_file( $temp_file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_write
				}
			}
			
			// Verify the file was created
			if ( ! file_exists( $zip_path ) ) { // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_write
				Freefactu_Logger::log( 'PclZip: ZIP file does not exist after creation!', 'error' );
				return new WP_Error( 'zip_file_missing', __( 'El archivo ZIP no se creó correctamente.', 'freefactu' ) );
			}
			
			$file_size = filesize( $zip_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_write
			Freefactu_Logger::log( 'PclZip: ZIP file created successfully. Size: ' . $file_size . ' bytes', 'info' );
			
			return $zip_path;
			
		} catch ( Exception $e ) {
			// Clean up temp files on error
			foreach ( $temp_files as $temp_file ) {
				if ( file_exists( $temp_file ) ) {
					wp_delete_file( $temp_file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_write
				}
			}
			Freefactu_Logger::log( 'PclZip exception: ' . $e->getMessage(), 'error' );
			return new WP_Error( 'pclzip_exception', $e->getMessage() );
		}
	}

	/**
	 * Add a directory recursively to a ZIP archive.
	 *
	 * @param ZipArchive $zip       ZipArchive instance.
	 * @param string     $source    Source directory path.
	 * @param string     $dest      Destination path in ZIP.
	 * @return void
	 */
	private static function add_directory_to_zip( $zip, $source, $dest ) {
		$source = rtrim( $source, '/\\' );
		
		if ( ! is_dir( $source ) ) {
			return;
		}

		$iterator = new RecursiveIteratorIterator(
			new RecursiveDirectoryIterator( $source, RecursiveDirectoryIterator::SKIP_DOTS ),
			RecursiveIteratorIterator::SELF_FIRST
		);

		foreach ( $iterator as $file ) {
			$file_path = $file->getPathname();
			$relative_path = substr( $file_path, strlen( $source ) + 1 );
			$zip_path = $dest . '/' . str_replace( '\\', '/', $relative_path );

			// Skip sensitive files
			if ( self::should_skip_file( $relative_path ) ) {
				continue;
			}

			if ( $file->isDir() ) {
				$zip->addEmptyDir( $zip_path );
			} else {
				$zip->addFile( $file_path, $zip_path );
			}
		}
	}

	/**
	 * Check if a file should be skipped in the ZIP archive.
	 *
	 * @param string $path Relative file path.
	 * @return bool True if file should be skipped.
	 */
	private static function should_skip_file( $path ) {
		$skip_patterns = array(
			'.git',
			'.svn',
			'node_modules',
			'.DS_Store',
			'Thumbs.db',
			'.env',
			'.log',
		);

		foreach ( $skip_patterns as $pattern ) {
			if ( strpos( $path, $pattern ) !== false ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Export Freefactu database tables.
	 *
	 * @return string|WP_Error SQL export or WP_Error.
	 */
	private static function export_database_tables() {
		global $wpdb;

		$tables = array(
			Freefactu_DB::get_table_name(),
			Freefactu_DB::get_declarations_table_name(),
		);

		// Add series table if it exists
		if ( class_exists( 'Freefactu_Series_Store' ) && method_exists( 'Freefactu_Series_Store', 'get_table_name' ) ) {
			$tables[] = Freefactu_Series_Store::get_table_name();
		}

		$export = "-- Freefactu Database Export\n";
		$export .= "-- Generated: " . current_time( 'mysql' ) . "\n";
		$export .= "-- WordPress: " . get_bloginfo( 'version' ) . "\n";
		$export .= "-- Plugin Version: " . FREEFACTU_VERSION . "\n\n";

		foreach ( $tables as $table ) {
			// Check if table exists
			$table_exists = $wpdb->get_var( $wpdb->prepare( "SHOW TABLES LIKE %s", $table ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			if ( ! $table_exists ) {
				continue;
			}

			$export .= "-- Table: {$table}\n";
			
			// Get create table statement
			// Get create table statement
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange
			$create_table = $wpdb->get_row( "SHOW CREATE TABLE " . esc_sql( $table ), ARRAY_A );
			if ( $create_table ) {
				$export .= $create_table['Create Table'] . ";\n\n";
			}

			// Get data
			// Get data
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$rows = $wpdb->get_results( "SELECT * FROM " . esc_sql( $table ), ARRAY_A );
			if ( $rows ) {
				foreach ( $rows as $row ) {
					$values = array_map( function( $v ) use ( $wpdb ) {
						if ( $v === null ) {
							return 'NULL';
						}
						return "'" . $wpdb->_real_escape( $v ) . "'";
					}, $row );

					$columns = '`' . implode( '`, `', array_keys( $row ) ) . '`';
					$export .= "INSERT INTO `{$table}` ({$columns}) VALUES (" . implode( ', ', $values ) . ");\n";
				}
				$export .= "\n";
			}
		}

		// Add WordPress options
		$export .= "-- Freefactu Settings\n";
		$options = get_option( 'freefactu_settings', array() );
		// Remove sensitive data
		unset( $options['certificate_password'] );
		$export .= "-- Settings: " . json_encode( $options, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE ) . "\n";

		return $export;
	}

	/**
	 * Generate manifest file for the ZIP archive.
	 *
	 * @param array $metadata Metadata.
	 * @return string Manifest content.
	 */
	private static function generate_manifest( $metadata = array() ) {
		$manifest = "FREEFACTU - SISTEMA INFORMÁTICO DE FACTURACIÓN (SIF)\n";
		$manifest .= str_repeat( "=", 60 ) . "\n\n";
		$manifest .= "Archivo generado: " . current_time( 'd/m/Y H:i:s' ) . "\n";
		$manifest .= "Versión del plugin: " . FREEFACTU_VERSION . "\n";
		$manifest .= "WordPress: " . get_bloginfo( 'version' ) . "\n";
		$manifest .= "PHP: " . phpversion() . "\n";
		$manifest .= "Sitio web: " . get_bloginfo( 'url' ) . "\n\n";

		if ( ! empty( $metadata['operator_name'] ) ) {
			$manifest .= "Operador: " . $metadata['operator_name'] . "\n";
		}
		if ( ! empty( $metadata['operator_nif'] ) ) {
			$manifest .= "NIF/CIF: " . $metadata['operator_nif'] . "\n";
		}

		$manifest .= "\n" . str_repeat( "-", 60 ) . "\n";
		$manifest .= "CONTENIDO DEL ARCHIVO\n";
		$manifest .= str_repeat( "-", 60 ) . "\n\n";
		$manifest .= "/plugins/freefactu/     - Código fuente del plugin\n";
		$manifest .= "/uploads/freefactu/     - Facturas PDF generadas\n";
		$manifest .= "/uploads/freefactu-certs/ - Certificados digitales\n";
		$manifest .= "database_export.sql     - Exportación de base de datos\n";
		$manifest .= "MANIFEST.txt            - Este archivo\n";

		return $manifest;
	}

	/**
	 * Get the URL for downloading a declaration PDF.
	 *
	 * @param int $declaration_id Declaration ID.
	 * @return string Download URL.
	 */
	public static function get_declaration_pdf_url( $declaration_id ) {
		return wp_nonce_url(
			admin_url( 'admin-post.php?action=freefactu_download_declaration_pdf&id=' . $declaration_id ),
			'freefactu_download_declaration_' . $declaration_id
		);
	}

	/**
	 * Get the URL for downloading a declaration ZIP.
	 *
	 * @param int $declaration_id Declaration ID.
	 * @return string Download URL.
	 */
	public static function get_declaration_zip_url( $declaration_id ) {
		return wp_nonce_url(
			admin_url( 'admin-post.php?action=freefactu_download_declaration_zip&id=' . $declaration_id ),
			'freefactu_download_declaration_' . $declaration_id
		);
	}

	/**
	 * Save declaration with PDF and ZIP generation.
	 *
	 * @param array $data Declaration data.
	 * @return int|WP_Error Declaration ID or WP_Error.
	 */
	public static function create_declaration( $data ) {
		// Generate the full declaration content if not provided
		if ( empty( $data['declaration_content'] ) ) {
			$data['declaration_content'] = self::generate_declaration_content( $data );
		}

		// Insert declaration into database
		$result = Freefactu_DB::insert_declaration( $data );
		
		if ( is_wp_error( $result ) ) {
			Freefactu_Logger::log( 'Declaration insert failed: ' . $result->get_error_message(), 'error' );
			return $result;
		}

		$declaration_id = $result;
		Freefactu_Logger::log( 'Declaration inserted with ID: ' . $declaration_id, 'info' );

		// Ensure storage directory exists
		self::ensure_declarations_dir();
		$declarations_dir = self::get_declarations_dir();
		Freefactu_Logger::log( 'Declarations directory: ' . $declarations_dir, 'info' );

		// Generate PDF
		$pdf_content = self::generate_declaration_pdf( $data['declaration_content'], $data );
		if ( is_wp_error( $pdf_content ) ) {
			Freefactu_Logger::log( 'PDF generation failed: ' . $pdf_content->get_error_message(), 'error' );
		} else {
			$pdf_filename = 'declaration-' . $declaration_id . '-' . gmdate( 'Ymd-His' ) . '.pdf';
			$pdf_path = $declarations_dir . '/' . $pdf_filename;
			$pdf_saved = Freefactu_Filesystem::put_contents( $pdf_path, $pdf_content );
			
			if ( $pdf_saved ) {
				Freefactu_Logger::log( 'PDF saved to: ' . $pdf_path, 'info' );
				// Update declaration with PDF path
				global $wpdb;
				$table_name = Freefactu_DB::get_declarations_table_name();
				$relative_pdf_path = 'freefactu-declarations/' . $pdf_filename;
				$wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
					$table_name,
					array( 'pdf_path' => $relative_pdf_path ),
					array( 'id' => $declaration_id ),
					array( '%s' ),
					array( '%d' )
				);
			} else {
				Freefactu_Logger::log( 'Failed to save PDF to: ' . $pdf_path, 'error' );
			}
		}

		// Generate ZIP
		Freefactu_Logger::log( 'Starting ZIP generation...', 'info' );
		$zip_result = self::generate_sif_zip( $declaration_id, $data );
		if ( is_wp_error( $zip_result ) ) {
			Freefactu_Logger::log( 'ZIP generation failed: ' . $zip_result->get_error_message(), 'error' );
		} else {
			Freefactu_Logger::log( 'ZIP generated at: ' . $zip_result, 'info' );
			$zip_filename = basename( $zip_result );
			$relative_zip_path = 'freefactu-declarations/' . $zip_filename;
			
			global $wpdb;
			$table_name = Freefactu_DB::get_declarations_table_name();
			$wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				$table_name,
				array( 'zip_path' => $relative_zip_path ),
				array( 'id' => $declaration_id ),
				array( '%s' ),
				array( '%d' )
			);
			Freefactu_Logger::log( 'ZIP path saved to database: ' . $relative_zip_path, 'info' );
		}

		return $declaration_id;
	}
}
