<?php
/**
 * Image model class.
 *
 * @class ImageModel
 * @package KimaAI\Models
 * @since 1.1.0
 */

declare(strict_types=1);

namespace KimaAI\Models;

defined('ABSPATH') || exit;

/**
 * Class ImageModel
 */
class ImageModel {

  use HasSchema;

  /**
   * The table name.
   *
   * @var string
   */
  public const TABLE = 'kimaai_images';

  /**
   * The table primary key.
   *
   * @var string
   */
  public const PRIMARY_KEY = 'id';

  /**
   * The table columns.
   *
   * @var array
   */
  public const COLUMNS = [
    'id' => 'BIGINT(20) NOT NULL AUTO_INCREMENT',
    'user_id' => 'BIGINT(20) NOT NULL',
    'ip' => 'VARCHAR(64) NOT NULL',
    'session_id' => 'VARCHAR(64) NOT NULL',
    'input' => 'LONGTEXT NOT NULL',
    'image_paths' => 'LONGTEXT NOT NULL',
    'created_at' => 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP',
  ];

  /**
   * The table name.
   *
   * @var string
   */
  private string $table_name;

  /**
   * ImageModel constructor.
   */
  public function __construct() {
    global $wpdb;
    $this->table_name = esc_sql($wpdb->prefix . self::TABLE);
  }

  /**
   * Inserts a new image record into the database.
   *
   * @param array $data The data to insert.
   *
   * @return int The ID of the inserted record.
   */
  public function insert(array $data): int {
    global $wpdb;

    $data = array_intersect_key($data, self::COLUMNS);

    // Array → JSON for storage.
    if (isset($data['image_paths']) && is_array($data['image_paths'])) {
      $data['image_paths'] = wp_json_encode($data['image_paths']);
    }

    if (isset($data['input']) && is_array($data['input'])) {
      $data['input'] = wp_json_encode($data['input']);
    }

    $data['created_at'] = current_time('mysql');

    // explicit formats = no manual esc_sql().
    $formats = [
      '%d', // id (auto).
      '%d', // user_id.
      '%s', // ip.
      '%s', // session_id.
      '%s', // input (JSON).
      '%s', // image_paths (JSON).
      '%s', // created_at.
    ];

    // phpcs:ignore
    $wpdb->insert($this->table_name, $data, $formats);

    return $wpdb->insert_id ? (int) $wpdb->insert_id : 0;
  }

  /**
   * Returns all image history records.
   *
   * @return array[] The image history records.
   */
  public function all(): array {
    global $wpdb;
    // phpcs:ignore
    $query = $wpdb->prepare("SELECT * FROM {$this->table_name} ORDER BY created_at DESC");
    // phpcs:ignore
    $results = $wpdb->get_results($query, ARRAY_A);
    return $results;
  }

  /**
   * Deletes an image history record by ID.
   *
   * @param int $id The ID of the record to delete.
   *
   * @return bool True on success, false on failure.
   */
  public function delete(int $id): bool {
    global $wpdb;

    // phpcs:ignore
    $result = $wpdb->delete(
      $this->table_name,
      ['id' => $id],
      ['%d']
    );

    return (bool) $result;
  }

  /**
   * Download a remote image and store it under wp‑content/uploads/kimaai-generated/.
   *
   * @param string $url The URL of the remote image.
   * @return string|null Public URL on success, null on failure.
   */
  public function saveRemoteImage(string $url): ?string {
    if (str_starts_with($url, 'data:')) {
      // Parse data URLs that contain a base64-encoded image payload.
      if (preg_match('#^data:(?<mime>[\w/\-\.+]+)?;base64,(?<data>[A-Za-z0-9+/=\s]+)$#', $url, $matches)) {
        $mime = $matches['mime'] ?? 'image/png';
        $b64 = preg_replace('/\s+/', '', $matches['data']); // strip whitespace
        return $this->saveBase64Image($b64, $mime);
      }
      return null;
    }

    $response = wp_remote_get($url, ['timeout' => 15]);

    if (is_wp_error($response)) {
      return null;
    }

    $body = wp_remote_retrieve_body($response);
    $content_type = wp_remote_retrieve_header($response, 'content-type');

    if (empty($body) || empty($content_type)) {
      return null;
    }

    $upload = wp_upload_dir();
    $subdir = 'kimaai-generated';
    $dir = trailingslashit($upload['basedir']) . $subdir;
    $dir_url = trailingslashit($upload['baseurl']) . $subdir;

    if (!wp_mkdir_p($dir)) {
      return null;
    }

    // Strip query‑string and look for an ext in the path.
    $path_part = wp_parse_url($url, PHP_URL_PATH);
    $basename = pathinfo($path_part, PATHINFO_BASENAME);

    // If the URL had no extension, derive one from the mime‑type.
    if (!preg_match('/\.(jpe?g|png|gif|webp|bmp)$/i', $basename)) {
      $mime_to_ext = [
        'image/jpeg' => 'jpg',
        'image/png' => 'png',
        'image/webp' => 'webp',
        'image/gif' => 'gif',
        'image/bmp' => 'bmp',
      ];
      $ext = $mime_to_ext[$content_type] ?? 'jpg';
      $basename = sanitize_file_name(wp_unique_filename($dir, 'kimaai-image.' . $ext));
    }
    else {
      $basename = sanitize_file_name(wp_unique_filename($dir, $basename));
    }

    $abs_path = trailingslashit($dir) . $basename;

    global $wp_filesystem;
    if (!$wp_filesystem) {
      require_once ABSPATH . 'wp-admin/includes/file.php';
      WP_Filesystem();
    }

    if (!$wp_filesystem->put_contents($abs_path, $body, FS_CHMOD_FILE)) {
      return null;
    }

    return trailingslashit($dir_url) . $basename;
  }

  /**
   * Save a base64-encoded image (optionally with a given mime type) to uploads/kimaai-generated/.
   *
   * @param string $b64 Base64 string (raw, not data URL).
   * @param string|null $mime Mime type, defaults to 'image/png'.
   * @param string|null $suggested Optional suggested filename (used for extension only).
   * @return string|null Public URL on success, null on failure.
   */
  public function saveBase64Image(string $b64, ?string $mime = 'image/png', ?string $suggested = null): ?string {
    // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- Decoding image bytes from a validated data URL, not executable code.
    $bytes = base64_decode($b64, true);
    if ($bytes === false || $bytes === '') {
      return null;
    }

    $upload = wp_upload_dir();
    $subdir = 'kimaai-generated';
    $dir = trailingslashit($upload['basedir']) . $subdir;
    $dir_url = trailingslashit($upload['baseurl']) . $subdir;

    if (!wp_mkdir_p($dir)) {
      return null;
    }

    // Pick extension based on mime/type or suggestion.
    $mime_to_ext = [
      'image/jpeg' => 'jpg',
      'image/png' => 'png',
      'image/webp' => 'webp',
      'image/gif' => 'gif',
      'image/bmp' => 'bmp',
    ];

    $ext = $mime_to_ext[$mime ?? ''] ?? 'png';

    // If we got a suggested filename, try to reuse its extension.
    if ($suggested && preg_match('/\.(jpe?g|png|gif|webp|bmp)$/i', $suggested, $matches)) {
      $ext = ltrim(strtolower($matches[0]), '.');
    }

    $basename = sanitize_file_name(wp_unique_filename($dir, 'kimaai-image.' . $ext));
    $abs_path = trailingslashit($dir) . $basename;

    global $wp_filesystem;
    if (!$wp_filesystem) {
      require_once ABSPATH . 'wp-admin/includes/file.php';
      WP_Filesystem();
    }

    if (!$wp_filesystem->put_contents($abs_path, $bytes, FS_CHMOD_FILE)) {
      return null;
    }

    return trailingslashit($dir_url) . $basename;
  }
}
