<?php
/**
 * REST endpoint for settings management.
 *
 * @since 1.0.0
 */

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

class ZINV_REST_Settings {

	private string $namespace;
	private string $rest_base;

	private const SETTINGS_SCHEMA = [
		'general' => [
			'company_name'  => '',
			'company_logo'  => '',
			'company_email' => '',
			'company_phone' => '',
			'tax_id'        => '',
			'address'       => '',
			'currency'      => 'USD',
		],
		'payments' => [
			'stripe_enabled'        => false,
			'stripe_mode'           => 'test',
			'stripe_test_key'       => '',
			'stripe_live_key'       => '',
			'stripe_webhook_secret' => '',
			// Free-tier: PayPal bookkeeping configuration.
			// Clients pay the merchant's PayPal account directly; the merchant
			// then records the receipt in Zoventic Invoices using method=paypal.
			'paypal_enabled'        => false,
			'paypal_email'          => '',
			'paypal_client_id'      => '',  // Optional — reserved for future Pro PayPal SDK integration.
			'paypal_environment'    => 'sandbox', // 'sandbox' | 'live'
			// Free-tier: Manual / cash payment configuration.
			'manual_enabled'        => true,
			'manual_instructions'   => '',  // Custom payment instructions shown to merchant.
		],
		'invoicing' => [
			'invoice_prefix'             => 'INV-',
			'invoice_starting_number'    => 1,
			'default_terms'              => 'Payment is due within 30 days.',
			'default_template'           => 'clean-sheet',
			'default_footer'             => '',
			'default_tax_rate'           => 0,
			// Free-tier invoice sent email template.
			'email_from_name'            => 'Your Business',
			'email_from_email'           => 'invoices@yourdomain.com',
			'email_invoice_subject'      => 'Invoice {invoice_number} from {business_name}',
			'email_invoice_body'         => "Dear {client_name},\n\nThank you for your business. We have issued invoice {invoice_number} for {amount}.\n\nInvoice Details:\n----------------------------------------\nInvoice Number: {invoice_number}\nTotal Amount:   {amount}\nDue Date:       {due_date}\n----------------------------------------\n\nA PDF copy of your invoice is attached to this email for your records.\n\nIf you have any questions, feel free to reply directly to this email.\n\nBest regards,\n{business_name}",
			// --- Template Builder: layout / branding ---
			'template_accent_color'      => '#000000',
			'template_secondary_color'   => '#607CEC',
			'template_show_logo'         => true,
			'template_logo_placement'    => 'left',
			'template_page_margin'       => 48,
			// --- Template Builder: typography ---
			'template_font_family'       => 'Helvetica',
			'template_body_font'         => 'Helvetica',
			'template_heading_size'      => 36,
			'template_body_size'         => 14,
			'template_line_height'       => 1.6,
			'template_text_color'        => 'rich-ink',
			// --- Template Builder: table ---
			'template_table_header_bg'   => '#f2f4f6',
			'template_header_bold'       => true,
			'template_header_italic'     => false,
			'template_header_caps'       => true,
			'template_row_padding'       => 20,
			'template_border_style'      => 'bottom',
			'template_alt_row_shading'   => false,
			'template_desc_align'        => 'left',
			'template_amount_align'      => 'right',
			// --- Template Builder: footer / elements ---
			'template_show_footer'       => true,
			'template_footer_layout'     => 'full',
			'template_show_signature'    => false,
			'template_signature_style'   => 'typed',
			// --- Watermark Engine ---
			'enable_watermark'           => false,
			'watermark_type'             => 'SAMPLE',
			'watermark_custom_text'      => '',
		],
	];

	public function __construct( string $namespace ) {
		$this->namespace = $namespace;
		$this->rest_base = 'settings';
	}

	public function register(): void {
		register_rest_route( $this->namespace, '/' . $this->rest_base, [
			[
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => [ $this, 'get_settings' ],
				'permission_callback' => [ $this, 'check_permission' ],
			],
			[
				'methods'             => WP_REST_Server::EDITABLE,
				'callback'            => [ $this, 'update_settings' ],
				'permission_callback' => [ $this, 'check_permission' ],
				'args'                => $this->get_update_params(),
			],
		] );

		register_rest_route( $this->namespace, '/' . $this->rest_base . '/test-email', [
			'methods'             => WP_REST_Server::CREATABLE,
			'callback'            => [ $this, 'send_test_email' ],
			'permission_callback' => [ $this, 'check_permission' ],
			'args'                => [
				'to' => [
					'type'              => 'string',
					'required'          => false,
					'sanitize_callback' => 'sanitize_email',
				],
			],
		] );
	}

	public function check_permission(): bool {
		return current_user_can( 'zinv_manage_settings' );
	}

	public function get_settings( WP_REST_Request $request ): WP_REST_Response {
		global $wpdb;
		$table = ( new ZINV_Database( 'zinv_settings' ) )->get_table_name();

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
		$rows = $wpdb->get_results( "SELECT setting_key, setting_value, setting_group FROM {$table}" );

		$settings = [];
		foreach ( self::SETTINGS_SCHEMA as $group => $defaults ) {
			$settings[ $group ] = $defaults;
		}

		foreach ( $rows as $row ) {
			$value = json_decode( $row->setting_value, true );
			if ( json_last_error() !== JSON_ERROR_NONE ) {
				$value = $row->setting_value;
			}
			if ( isset( $settings[ $row->setting_group ] ) ) {
				if ( is_string( $value ) && '' === trim( $value ) ) {
					$default_val = self::SETTINGS_SCHEMA[ $row->setting_group ][ $row->setting_key ] ?? '';
					if ( '' !== $default_val ) {
						$value = $default_val;
					}
				}
				$settings[ $row->setting_group ][ $row->setting_key ] = $value;
			}
		}

		// Never return raw secret key material — mask with placeholder.
		$sensitive = [ 'stripe_test_key', 'stripe_live_key', 'stripe_webhook_secret' ];
		foreach ( $sensitive as $k ) {
			if ( ! empty( $settings['payments'][ $k ] ) ) {
				$settings['payments'][ $k ] = '••••••••';
			}
		}

		return new WP_REST_Response( $settings, 200 );
	}

	public function update_settings( WP_REST_Request $request ): WP_REST_Response|WP_Error {
		global $wpdb;
		$table = ( new ZINV_Database( 'zinv_settings' ) )->get_table_name();
		$body  = $request->get_json_params();

		if ( ! is_array( $body ) ) {
			return new WP_Error(
				'zinv_settings_invalid',
				__( 'Invalid settings data.', 'zoventic-invoices' ),
				[ 'status' => 400 ]
			);
		}

		// Pre-fetch all existing settings in a single query to eliminate N+1 DB lookups.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
		$existing_rows = $wpdb->get_results( "SELECT id, setting_key, setting_group FROM {$table}" );
		$existing_map  = [];
		foreach ( (array) $existing_rows as $row ) {
			$existing_map[ $row->setting_group . '.' . $row->setting_key ] = (int) $row->id;
		}

		$saved_count    = 0;
		$unknown_groups = [];
		$unknown_keys   = [];

		foreach ( $body as $group => $settings ) {
			if ( ! isset( self::SETTINGS_SCHEMA[ $group ] ) ) {
				$unknown_groups[] = (string) $group;
				continue;
			}
			if ( ! is_array( $settings ) ) {
				continue;
			}

			foreach ( $settings as $key => $value ) {
				if ( ! array_key_exists( $key, self::SETTINGS_SCHEMA[ $group ] ) ) {
					$unknown_keys[] = $group . '.' . (string) $key;
					continue;
				}

				// Skip masked placeholder — never persist it back.
				if ( 'payments' === $group && '••••••••' === $value ) {
					continue;
				}

				// Reject an unknown default_template slug so the renderer is
				// never handed a value it would silently fall back from.
				if (
					'invoicing' === $group
					&& 'default_template' === $key
					&& class_exists( 'ZINV_PDF_Generator' )
					&& ! ZINV_PDF_Generator::is_valid_template( sanitize_text_field( (string) $value ) )
				) {
					return new WP_Error(
						'zinv_settings_invalid_template',
						__( 'The selected default template is not available.', 'zoventic-invoices' ),
						[ 'status' => 400 ]
					);
				}

				if (
					'invoicing' === $group
					&& in_array( $key, [ 'template_font_family', 'template_body_font' ], true )
					&& class_exists( 'ZINV_Template_Style' )
					&& ! ZINV_Template_Style::is_valid_pdf_font( sanitize_text_field( (string) $value ) )
				) {
					return new WP_Error(
						'zinv_settings_invalid_font',
						__( 'The selected font is not available for PDF export.', 'zoventic-invoices' ),
						[ 'status' => 400 ]
					);
				}

				$sanitized_value = $this->sanitize_setting_value( $group, $key, $value );
				$encoded         = wp_json_encode( $sanitized_value );

				// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
				$map_key = $group . '.' . $key;
				if ( isset( $existing_map[ $map_key ] ) ) {
					$wpdb->update(
						$table,
						[ 'setting_value' => $encoded, 'updated_at' => gmdate( 'Y-m-d H:i:s' ) ],
						[ 'id' => $existing_map[ $map_key ] ],
						[ '%s', '%s' ],
						[ '%d' ]
					);
				} else {
					$wpdb->insert( $table, [
						'setting_key'   => $key,
						'setting_value' => $encoded,
						'setting_group' => $group,
						'updated_at'    => gmdate( 'Y-m-d H:i:s' ),
					], [ '%s', '%s', '%s', '%s' ] );
				}
				// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
				$saved_count++;
			}
		}

		// Only fire the saved hook and flush cache if at least one row was written.
		if ( $saved_count > 0 ) {
			if ( class_exists( 'ZINV_Settings' ) ) {
				ZINV_Settings::flush_cache();
			}
			if ( class_exists( 'ZINV_Pdf_Generator' ) ) {
				ZINV_Pdf_Generator::delete_all_cached_pdfs();
			}
			do_action( 'zinv_settings_saved' );
		}

		return new WP_REST_Response( [
			'saved'          => $saved_count > 0,
			'saved_count'    => $saved_count,
			'unknown_groups' => $unknown_groups,
			'unknown_keys'   => $unknown_keys,
		], 200 );
	}

	public function send_test_email( WP_REST_Request $request ): WP_REST_Response {
		$to           = sanitize_email( $request->get_param( 'to' ) ?? '' );
		$current_user = wp_get_current_user();
		$allowed_tos  = array_filter( [ get_option( 'admin_email' ), $current_user ? $current_user->user_email : '' ] );

		if ( empty( $to ) || ! in_array( $to, $allowed_tos, true ) ) {
			$to = get_option( 'admin_email' );
		}

		$from_name  = get_option( 'blogname', 'zoventic-invoices' );
		$from_email = get_option( 'admin_email' );

		$headers = [
			'Content-Type: text/plain; charset=UTF-8',
			"From: {$from_name} <{$from_email}>",
		];

		$subject = sprintf( '[%s] Test Email from Zoventic Invoices', get_option( 'blogname' ) );
		$message = sprintf(
			"Hi,\n\nThis is a test email from Zoventic Invoices to confirm your email configuration is working.\n\nSent to: %s\nSite: %s\nTime: %s\n",
			$to,
			get_bloginfo( 'url' ),
			current_time( 'mysql' )
		);

		$sent = wp_mail( $to, $subject, $message, $headers );

		if ( $sent ) {
			return new WP_REST_Response( [ 'sent' => true, 'to' => $to ], 200 );
		}

		return new WP_REST_Response(
			[ 'sent' => false, 'message' => __( 'wp_mail() returned false. Check your server email configuration.', 'zoventic-invoices' ) ],
			500
		);
	}

	private function sanitize_setting_value( string $group, string $key, mixed $value ): mixed {
		$default = self::SETTINGS_SCHEMA[ $group ][ $key ] ?? null;

		if ( is_bool( $default ) ) {
			return (bool) $value;
		}

		if ( is_int( $default ) || is_float( $default ) ) {
			return is_numeric( $value ) ? (float) $value : 0;
		}

		if ( is_string( $default ) ) {
			if ( ! is_string( $value ) && ! is_numeric( $value ) ) {
				$value = '';
			}
			$value_str = (string) $value;
			if ( in_array( $key, [ 'company_email', 'paypal_email', 'email_from_email' ], true ) ) {
				return sanitize_email( $value_str );
			}
			if ( 'company_logo' === $key ) {
				return esc_url_raw( $value_str );
			}
			if ( in_array( $key, [ 'address', 'default_terms', 'manual_instructions', 'email_invoice_body' ], true ) ) {
				return sanitize_textarea_field( $value_str );
			}
			return sanitize_text_field( $value_str );
		}

		return $value;
	}

	private function get_update_params(): array {
		$params = [];
		foreach ( self::SETTINGS_SCHEMA as $group => $fields ) {
			$params[ $group ] = [
				'required'          => false,
				'type'              => 'object',
				'sanitize_callback' => fn( $v ) => is_array( $v ) ? $v : [],
				'validate_callback' => fn( $v ) => is_array( $v ),
			];
		}
		return $params;
	}
}
