<?php
/**
 * Doc sheet dau file .xlsx (Zip + XML, khong can Composer).
 *
 * @package DatvetaucaotocBooking
 */

declare(strict_types=1);

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

final class Datvetau_Booking_Xlsx_Reader {

	/**
	 * @return array{ok:bool,rows:array<int,array<int,string>>,error:string}
	 */
	public static function read_first_sheet( string $file_path ): array {
		if ( ! class_exists( ZipArchive::class ) ) {
			return array(
				'ok'    => false,
				'rows'  => array(),
				'error' => __( 'May chu chua bat ZipArchive — khong doc duoc Excel.', 'datvetaucaotoc-booking' ),
			);
		}

		$zip = new ZipArchive();
		if ( true !== $zip->open( $file_path ) ) {
			return array(
				'ok'    => false,
				'rows'  => array(),
				'error' => __( 'Khong mo duoc file Excel (.xlsx).', 'datvetaucaotoc-booking' ),
			);
		}

		$shared_strings = self::read_shared_strings( $zip );
		$sheet_xml      = self::find_first_sheet_xml( $zip );
		if ( ! $sheet_xml ) {
			$zip->close();
			return array(
				'ok'    => false,
				'rows'  => array(),
				'error' => __( 'File Excel khong co bang du lieu.', 'datvetaucaotoc-booking' ),
			);
		}

		$rows = self::parse_sheet_rows( $sheet_xml, $shared_strings );
		$zip->close();

		return array(
			'ok'    => true,
			'rows'  => $rows,
			'error' => '',
		);
	}

	/**
	 * @return list<string>
	 */
	private static function read_shared_strings( ZipArchive $zip ): array {
		$xml = $zip->getFromName( 'xl/sharedStrings.xml' );
		if ( false === $xml || '' === $xml ) {
			return array();
		}
		$doc = simplexml_load_string( $xml );
		if ( false === $doc ) {
			return array();
		}
		$doc->registerXPathNamespace( 'm', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main' );
		$out = array();
		foreach ( $doc->si as $si ) {
			if ( isset( $si->t ) ) {
				$out[] = (string) $si->t;
				continue;
			}
			$parts = array();
			foreach ( $si->r as $run ) {
				$parts[] = (string) $run->t;
			}
			$out[] = implode( '', $parts );
		}
		return $out;
	}

	private static function find_first_sheet_xml( ZipArchive $zip ): ?string {
		$workbook = $zip->getFromName( 'xl/workbook.xml' );
		$rels     = $zip->getFromName( 'xl/_rels/workbook.xml.rels' );
		if ( false === $workbook || false === $rels ) {
			$fallback = $zip->getFromName( 'xl/worksheets/sheet1.xml' );
			return false !== $fallback ? $fallback : null;
		}
		$rels_doc = simplexml_load_string( $rels );
		if ( false === $rels_doc ) {
			return null;
		}
		$rels_doc->registerXPathNamespace( 'r', 'http://schemas.openxmlformats.org/package/2006/relationships' );
		$target = null;
		foreach ( $rels_doc->Relationship as $rel ) {
			$type = (string) $rel['Type'];
			if ( str_ends_with( $type, '/worksheet' ) ) {
				$target = (string) $rel['Target'];
				break;
			}
		}
		if ( ! $target ) {
			return null;
		}
		$path = 'xl/' . ltrim( str_replace( '..', '', $target ), '/' );
		$xml  = $zip->getFromName( $path );
		if ( false === $xml ) {
			$xml = $zip->getFromName( 'xl/worksheets/sheet1.xml' );
		}
		return false !== $xml ? $xml : null;
	}

	/**
	 * @param list<string> $shared_strings
	 * @return array<int,array<int,string>>
	 */
	private static function parse_sheet_rows( string $sheet_xml, array $shared_strings ): array {
		$doc = simplexml_load_string( $sheet_xml );
		if ( false === $doc ) {
			return array();
		}
		$doc->registerXPathNamespace( 'm', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main' );
		$rows_out = array();
		foreach ( $doc->sheetData->row as $row ) {
			$row_index = (int) $row['r'];
			$cells     = array();
			foreach ( $row->c as $cell ) {
				$ref   = (string) $cell['r'];
				$col_i = self::column_index_from_ref( $ref );
				$type  = (string) $cell['t'];
				$value = isset( $cell->v ) ? (string) $cell->v : '';
				if ( 's' === $type && '' !== $value && isset( $shared_strings[ (int) $value ] ) ) {
					$value = $shared_strings[ (int) $value ];
				} elseif ( 'inlineStr' === $type && isset( $cell->is->t ) ) {
					$value = (string) $cell->is->t;
				}
				$cells[ $col_i ] = $value;
			}
			if ( ! $cells ) {
				continue;
			}
			$max = max( array_keys( $cells ) );
			$line = array();
			for ( $i = 0; $i <= $max; ++$i ) {
				$line[] = isset( $cells[ $i ] ) ? (string) $cells[ $i ] : '';
			}
			$rows_out[ $row_index ] = $line;
		}
		ksort( $rows_out );
		return array_values( $rows_out );
	}

	private static function column_index_from_ref( string $ref ): int {
		if ( ! preg_match( '/^([A-Z]+)/', $ref, $m ) ) {
			return 0;
		}
		$letters = $m[1];
		$index   = 0;
		$len     = strlen( $letters );
		for ( $i = 0; $i < $len; ++$i ) {
			$index = $index * 26 + ( ord( $letters[ $i ] ) - ord( 'A' ) + 1 );
		}
		return $index - 1;
	}
}
