<?php
/**
 * Full data exporter (data portability)
 *
 * Produces a single JSON payload covering the entire plugin dataset
 * (courses, modules, slides, custom templates, memory variables,
 * memory definitions, safe settings). Intended for the admin Export Tool
 * and for Pro -> Free downgrade backups. API keys are never exported.
 *
 * The export envelope format is versioned as `slidemind-export-v1`.
 *
 * @package SlideMind\Core
 * @since   0.5.0
 */

namespace SlideMind\Core;

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

class FullExporter {

	const FORMAT_VERSION = 'slidemind-export-v1';

	const VALID_SECTIONS = [
		'courses',
		'modules',
		'slides',
		'templates',
		'memory_variables',
		'memory_definitions',
		'settings',
	];

	/**
	 * @var ConfigRepository
	 */
	private $config_repository;

	/**
	 * @var TemplateManager
	 */
	private $template_manager;

	public function __construct( ConfigRepository $config_repository, TemplateManager $template_manager ) {
		$this->config_repository = $config_repository;
		$this->template_manager  = $template_manager;
	}

	/**
	 * Builds the full export payload for the requested sections.
	 *
	 * @param string[]             $sections            Sections to include. Unknown entries are ignored.
	 * @param array<string, int[]> $item_ids_by_section Optional allowlist of source IDs per section.
	 *                                                  Empty or missing entry = include all items of that section.
	 *                                                  Only applies to `courses`, `modules`, `slides`, `templates`.
	 * @return array
	 */
	public function export( array $sections, array $item_ids_by_section = [] ): array {
		$sections = array_values( array_intersect( self::VALID_SECTIONS, $sections ) );

		$payload = [
			'format'         => self::FORMAT_VERSION,
			'generated_at'   => gmdate( 'c' ),
			'site_url'       => home_url( '/' ),
			'plugin_version' => defined( 'SLIDEMIND_VERSION' ) ? SLIDEMIND_VERSION : '',
			'sections'       => [],
			'stats'          => [],
		];

		foreach ( $sections as $section ) {
			$items = $this->build_section( $section );
			if ( is_array( $items ) && ! empty( $item_ids_by_section[ $section ] ) && is_array( $item_ids_by_section[ $section ] ) ) {
				$allowed = array_fill_keys( array_map( 'intval', $item_ids_by_section[ $section ] ), true );
				$items   = array_values( array_filter( $items, static function ( $item ) use ( $allowed ) {
					$id = isset( $item['id'] ) ? (int) $item['id'] : 0;
					return $id > 0 && isset( $allowed[ $id ] );
				} ) );
			}
			$payload['sections'][ $section ] = $items;
			$payload['stats'][ $section ]    = is_array( $items ) ? count( $items ) : (int) ! empty( $items );
		}

		return $payload;
	}

	/**
	 * Convenience wrapper returning a pretty-printed JSON string.
	 *
	 * @param string[]             $sections            Sections to include.
	 * @param array<string, int[]> $item_ids_by_section Optional allowlist of source IDs per section.
	 * @return string
	 */
	public function export_json( array $sections, array $item_ids_by_section = [] ): string {
		$payload = $this->export( $sections, $item_ids_by_section );
		return (string) wp_json_encode( $payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT );
	}

	/**
	 * Returns a stable filename for the export download.
	 */
	public function suggested_filename(): string {
		$host = wp_parse_url( home_url( '/' ), PHP_URL_HOST );
		$host = sanitize_key( (string) $host );
		$date = gmdate( 'Y-m-d' );
		return 'slidemind-export-' . ( $host !== '' ? $host . '-' : '' ) . $date . '.json';
	}

	// -------------------------------------------------------------------
	// Section builders
	// -------------------------------------------------------------------

	private function build_section( string $section ) {
		switch ( $section ) {
			case 'courses':
				return $this->collect_courses();
			case 'modules':
				return $this->collect_modules();
			case 'slides':
				return $this->collect_slides();
			case 'templates':
				return $this->collect_custom_templates();
			case 'memory_variables':
				return $this->collect_memory_variables();
			case 'memory_definitions':
				return $this->collect_memory_definitions();
			case 'settings':
				return $this->collect_safe_settings();
		}
		return [];
	}

	private function collect_courses(): array {
		$items = [];
		foreach ( $this->config_repository->get_courses( 'any' ) as $course ) {
			$items[] = [
				'id'         => (int) $course['id'],
				'title'      => $course['title'] ?? '',
				'status'     => $course['status'] ?? 'publish',
				'theme'      => $course['theme'] ?? '',
				'objectives' => $course['objectives'] ?? '',
				'content'    => $course['content'] ?? '',
			];
		}
		return $items;
	}

	private function collect_modules(): array {
		$items = [];
		foreach ( $this->config_repository->get_all_modules( 'any' ) as $module ) {
			$items[] = [
				'id'                  => (int) $module['id'],
				'title'               => $module['title'] ?? '',
				'status'              => $module['status'] ?? 'publish',
				'subject'             => $module['subject'] ?? '',
				'context'             => $module['context'] ?? '',
				'parent_course_id'    => (int) ( $module['parent_course_id'] ?? 0 ),
				'parent_course_title' => $module['parent_course_title'] ?? '',
			];
		}
		return $items;
	}

	private function collect_slides(): array {
		$items = [];
		foreach ( $this->config_repository->get_all_slides( 'any' ) as $slide ) {
			$items[] = [
				'id'                    => (int) $slide['id'],
				'title'                 => $slide['title'] ?? '',
				'status'                => $slide['status'] ?? 'publish',
				'slide_number'          => $slide['slide_number'] ?? null,
				'model_id'              => $slide['model_id'] ?? '',
				'system_prompt'         => $slide['system_prompt'] ?? '',
				'temperature'           => isset( $slide['temperature'] ) ? (float) $slide['temperature'] : 0.7,
				'max_tokens'            => isset( $slide['max_tokens'] ) ? (int) $slide['max_tokens'] : 1024,
				'variables'             => $slide['variables'] ?: [],
				'override_course'       => $slide['override_course'] ?: [],
				'override_module'       => $slide['override_module'] ?: [],
				'output_variables'      => $slide['output_variables'] ?: [],
				'notes'                 => $slide['notes'] ?? '',
				'widget_mode'           => $slide['widget_mode'] ?? 'qa',
				'max_turns'             => isset( $slide['max_turns'] ) ? (int) $slide['max_turns'] : 0,
				'closing_message'       => $slide['closing_message'] ?? '',
				'opening_message'       => $slide['opening_message'] ?? '',
				'graceful_close'        => $slide['graceful_close'] ?? '0',
				'graceful_close_prompt' => $slide['graceful_close_prompt'] ?? '',
				'parent_course_id'      => (int) ( $slide['parent_course_id'] ?? 0 ),
				'parent_module_id'      => (int) ( $slide['parent_module_id'] ?? 0 ),
				'parent_course_title'   => $slide['parent_course_title'] ?? '',
				'parent_module_title'   => $slide['parent_module_title'] ?? '',
				// api_key is never exported (sensitive, site-specific, encrypted at rest).
			];
		}
		return $items;
	}

	private function collect_custom_templates(): array {
		$items = [];
		foreach ( $this->template_manager->get_all() as $tpl ) {
			if ( ! empty( $tpl['is_default'] ) ) {
				continue;
			}
			$slug    = (string) ( $tpl['slug'] ?? '' );
			$items[] = [
				'id'               => $slug !== '' ? crc32( $slug ) : 0, // stable within a site, for UI selection
				'title'            => $tpl['title'] ?? '',
				'slug'             => $slug,
				'description'      => $tpl['description'] ?? '',
				'icon'             => $tpl['icon'] ?? '',
				'system_prompt'    => $tpl['system_prompt'] ?? '',
				'temperature'      => isset( $tpl['temperature'] ) ? (float) $tpl['temperature'] : 0.7,
				'max_tokens'       => isset( $tpl['max_tokens'] ) ? (int) $tpl['max_tokens'] : 1024,
				'variables'        => $tpl['variables'] ?? [],
				'widget_mode'      => $tpl['widget_mode'] ?? 'chat',
				'max_turns'        => $tpl['max_turns'] ?? 0,
				'closing_message'  => $tpl['closing_message'] ?? '',
				'opening_message'  => $tpl['opening_message'] ?? '',
				'graceful_close'   => $tpl['graceful_close'] ?? false,
				'output_variables' => $tpl['output_variables'] ?? [],
			];
		}
		return $items;
	}

	private function collect_memory_variables(): array {
		global $wpdb;
		$table = $wpdb->prefix . 'slidemind_learner_memory';

		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Data export query on plugin-owned table.
		$rows = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT id, wp_user_id, session_id, scope, scope_id, variable_name, variable_value, created_at, updated_at
				 FROM %i
				 ORDER BY wp_user_id ASC, session_id ASC, scope ASC, scope_id ASC, variable_name ASC",
				$table
			),
			ARRAY_A
		);
		// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching

		if ( ! is_array( $rows ) ) {
			return [];
		}

		$items = [];
		foreach ( $rows as $row ) {
			$items[] = [
				'wp_user_id'     => (int) $row['wp_user_id'],
				'session_id'     => (string) $row['session_id'],
				'scope'          => (string) $row['scope'],
				'scope_id'       => (int) $row['scope_id'],
				'variable_name'  => (string) $row['variable_name'],
				'variable_value' => (string) $row['variable_value'],
				'created_at'     => (string) $row['created_at'],
				'updated_at'     => (string) $row['updated_at'],
			];
		}
		return $items;
	}

	private function collect_memory_definitions(): array {
		$defs = get_option( 'slidemind_memory_definitions', [] );
		return is_array( $defs ) ? array_values( $defs ) : [];
	}

	/**
	 * Returns the safe subset of plugin settings.
	 *
	 * Excludes all API keys and transient test-mode flags.
	 *
	 * @return array
	 */
	private function collect_safe_settings(): array {
		$settings = get_option( 'slidemind_settings', [] );
		if ( ! is_array( $settings ) ) {
			$settings = [];
		}

		// Drop anything that could look like an API key or a transient flag.
		unset(
			$settings['cors_test_mode'],
			$settings['cors_test_mode_until']
		);
		// Match credential-shaped keys without false positives like default_max_tokens.
		foreach ( array_keys( $settings ) as $key ) {
			if ( preg_match( '/(?:^|_)(?:api[_-]?key|secret|token|access_token|auth_token)(?:$|_)/i', (string) $key ) ) {
				unset( $settings[ $key ] );
			}
		}

		$settings['global_provider'] = get_option( 'slidemind_global_provider', 'openrouter' );

		return $settings;
	}
}
