<?php
/**
 * CSV import for alt text updates.
 *
 * @package AIAltTextGenerator
 * @since 1.7.0
 */

namespace AIAltTextGenerator;

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

/**
 * @since 1.7.0
 */
class Importer {

	private const MAX_IMPORT_BYTES = 5242880;
	private const MAX_IMPORT_ROWS  = 5000;

	/**
	 * Import alt text rows from an uploaded CSV file.
	 *
	 * @param array<string, mixed> $file $_FILES entry.
	 * @return array{imported: int, skipped: int, errors: list<string>}
	 */
	public static function importFromUpload( array $file ): array {
		if ( empty( $file['tmp_name'] ) || ! is_uploaded_file( $file['tmp_name'] ) ) {
			return [
				'imported' => 0,
				'skipped'  => 0,
				'errors'   => [ __( 'No file uploaded.', 'wppowersuite-lite' ) ],
			];
		}

		if ( ! empty( $file['size'] ) && (int) $file['size'] > self::MAX_IMPORT_BYTES ) {
			return [
				'imported' => 0,
				'skipped'  => 0,
				'errors'   => [ __( 'CSV file is too large. Maximum size is 5 MB.', 'wppowersuite-lite' ) ],
			];
		}

		$filename = isset( $file['name'] ) ? (string) $file['name'] : '';
		$checked  = wp_check_filetype_and_ext( (string) $file['tmp_name'], $filename );
		if ( ( $checked['ext'] ?? '' ) !== 'csv' ) {
			return [
				'imported' => 0,
				'skipped'  => 0,
				'errors'   => [ __( 'Only CSV files are allowed.', 'wppowersuite-lite' ) ],
			];
		}

		return self::importFromPath( (string) $file['tmp_name'] );
	}

	/**
	 * Import alt text rows from a readable CSV path.
	 *
	 * @return array{imported: int, skipped: int, errors: list<string>}
	 */
	public static function importFromPath( string $path ): array {
		$result = [
			'imported' => 0,
			'skipped'  => 0,
			'errors'   => [],
		];

		if ( ! is_readable( $path ) ) {
			$result['errors'][] = __( 'File not readable.', 'wppowersuite-lite' );
			return $result;
		}

		$handle = fopen( $path, 'r' );
		if ( $handle === false ) {
			$result['errors'][] = __( 'Could not read CSV file.', 'wppowersuite-lite' );
			return $result;
		}

		$header = fgetcsv( $handle );
		if ( ! is_array( $header ) ) {
			fclose( $handle );
			$result['errors'][] = __( 'CSV file is empty.', 'wppowersuite-lite' );
			return $result;
		}

		$map = self::mapColumns( $header );
		if ( ! isset( $map['attachment_id'] ) ) {
			fclose( $handle );
			$result['errors'][] = __( 'CSV must include an attachment_id column.', 'wppowersuite-lite' );
			return $result;
		}

		$alt_key = $map['alt_text'] ?? $map['current_alt'] ?? $map['new_alt'] ?? null;
		if ( $alt_key === null ) {
			fclose( $handle );
			$result['errors'][] = __( 'CSV must include alt_text, current_alt, or new_alt column.', 'wppowersuite-lite' );
			return $result;
		}

		$row_num = 1;
		while ( ( $row = fgetcsv( $handle ) ) !== false ) {
			++$row_num;
			if ( $row_num > self::MAX_IMPORT_ROWS + 1 ) {
				$result['errors'][] = __( 'CSV row limit exceeded. Import stopped at 5,000 rows.', 'wppowersuite-lite' );
				break;
			}
			if ( ! is_array( $row ) || $row === [ null ] ) {
				continue;
			}

			$attachment_id = isset( $row[ $map['attachment_id'] ] ) ? (int) $row[ $map['attachment_id'] ] : 0;
			$alt_text      = isset( $row[ $alt_key ] ) ? sanitize_text_field( (string) $row[ $alt_key ] ) : '';

			if ( $attachment_id <= 0 ) {
				++$result['skipped'];
				continue;
			}

			if ( get_post_type( $attachment_id ) !== 'attachment' ) {
				$result['errors'][] = sprintf(
					/* translators: 1: row number, 2: attachment ID */
					__( 'Row %1$d: #%2$d is not an attachment.', 'wppowersuite-lite' ),
					$row_num,
					$attachment_id
				);
				++$result['skipped'];
				continue;
			}

			if ( ! current_user_can( 'edit_post', $attachment_id ) ) {
				$result['errors'][] = sprintf(
					/* translators: 1: row number, 2: attachment ID */
					__( 'Row %1$d: permission denied for #%2$d.', 'wppowersuite-lite' ),
					$row_num,
					$attachment_id
				);
				++$result['skipped'];
				continue;
			}

			AttachmentMeta::saveAltText( $attachment_id, $alt_text );
			++$result['imported'];
		}

		fclose( $handle );
		Scanner::invalidateCache();
		DuplicateDetector::invalidateCache();

		return $result;
	}

	/**
	 * @param list<string|null> $header
	 * @return array<string, int>
	 */
	private static function mapColumns( array $header ): array {
		$map = [];
		foreach ( $header as $index => $column ) {
			$key = strtolower( trim( (string) $column ) );
			if ( $key !== '' ) {
				$map[ $key ] = (int) $index;
			}
		}

		return $map;
	}
}
