<?php
/**
 * Coupon campaign CRUD + WC sync.
 *
 * @package SuiteMkt3k
 */

namespace SuiteMkt3k\Modules\Coupons;

use SuiteMkt3k\Compat\WooCommerce_Check;
use SuiteMkt3k\Installer;
use SuiteMkt3k\Modules\Settings\Settings_Manager;
use SuiteMkt3k\Security\Capabilities;
use SuiteMkt3k\Security\Nonces;
use SuiteMkt3k\Utils\Logger;
use SuiteMkt3k\Utils\Sanitizer;
use SuiteMkt3k\Utils\Validator;

defined( 'ABSPATH' ) || exit;
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching

final class Coupon_Manager {

	public const FORMAT_CLASSIC = 'classic';
	public const FORMAT_OPTIN   = 'optin';

	public const STATUS_DRAFT     = 'draft';
	public const STATUS_SCHEDULED = 'scheduled';
	public const STATUS_ACTIVE    = 'active';
	public const STATUS_PAUSED    = 'paused';
	public const STATUS_EXPIRED   = 'expired';

	public static function statuses(): array {
		return array(
			self::STATUS_DRAFT     => __( 'Draft', 'suitemkt3k' ),
			self::STATUS_SCHEDULED => __( 'Scheduled', 'suitemkt3k' ),
			self::STATUS_ACTIVE    => __( 'Active', 'suitemkt3k' ),
			self::STATUS_PAUSED    => __( 'Paused', 'suitemkt3k' ),
			self::STATUS_EXPIRED   => __( 'Expired', 'suitemkt3k' ),
		);
	}

	public static function status_label( string $status ): string {
		$map = self::statuses();
		return $map[ $status ] ?? $status;
	}

	public static function register(): void {
		add_action( 'admin_post_suitemkt3k_coupon_save', array( __CLASS__, 'handle_save' ) );
		add_action( 'admin_post_suitemkt3k_coupon_delete', array( __CLASS__, 'handle_delete' ) );
		add_action( 'admin_post_suitemkt3k_coupon_duplicate', array( __CLASS__, 'handle_duplicate' ) );
		add_action( 'admin_post_suitemkt3k_coupon_toggle', array( __CLASS__, 'handle_toggle' ) );
	}

	public static function table(): string {
		return Installer::table( 'coupons' );
	}

	public static function design_table(): string {
		return Installer::table( 'coupon_designs' );
	}

	public static function list( int $limit = 100 ): array {
		global $wpdb;
		$limit = max( 1, min( 500, $limit ) );
		$rows = $wpdb->get_results(
			$wpdb->prepare(
				'SELECT c.*, d.design_json FROM %i c LEFT JOIN %i d ON d.coupon_id = c.id ORDER BY c.created_at DESC LIMIT %d',
				self::table(),
				self::design_table(),
				$limit
			),
			ARRAY_A
		) ?: array();
		return array_map( array( __CLASS__, 'inflate' ), $rows );
	}

	public static function get( int $id ): ?array {
		global $wpdb;
		$row = $wpdb->get_row(
			$wpdb->prepare(
				'SELECT c.*, d.design_json FROM %i c LEFT JOIN %i d ON d.coupon_id = c.id WHERE c.id = %d',
				self::table(),
				self::design_table(),
				$id
			),
			ARRAY_A
		);
		return $row ? self::inflate( $row ) : null;
	}

	private static function inflate( array $row ): array {
		$design = array();
		if ( ! empty( $row['design_json'] ) ) {
			$decoded = json_decode( (string) $row['design_json'], true );
			if ( is_array( $decoded ) ) {
				$design = $decoded;
			}
		}
		unset( $row['design_json'] );
		$row['design'] = self::default_design( $design );

		// Normalize the discount value for display: strip trailing zeros so the React
		// designer never shows "10.000000" when the user saved "10".
		if ( isset( $row['discount_value'] ) ) {
			$row['discount_value'] = self::format_amount( (float) $row['discount_value'], (string) ( $row['discount_type'] ?? 'percent' ) );
		}
		return $row;
	}

	/**
	 * Format a discount value: integer for %, up to 2 decimals for currencies
	 * (never trailing zeros). Returns a numeric string ready for both display
	 * and JS-side parsing.
	 */
	public static function format_amount( float $value, string $type ): string {
		if ( 'percent' === $type ) {
			return (string) (int) round( $value );
		}
		$str = number_format( $value, 2, '.', '' );
		// Strip trailing zeros and a dangling dot.
		$str = rtrim( rtrim( $str, '0' ), '.' );
		return '' === $str ? '0' : $str;
	}

	public static function default_design( array $overrides = array() ): array {
		$fallback_bg   = '#2AB2E5';
		$fallback_text = '#FFFFFF';
		$bg            = Sanitizer::hex_color( (string) Settings_Manager::get( 'coupons', 'default_bg', '' ) );
		$text          = Sanitizer::hex_color( (string) Settings_Manager::get( 'coupons', 'default_text', '' ) );
		if ( '' === $bg ) {
			$bg = $fallback_bg;
		}
		if ( '' === $text ) {
			$text = $fallback_text;
		}
		$popup_delay = max( 0, min( 60, (int) Settings_Manager::get( 'coupons', 'popup_delay', 4 ) ) );
		$cookie_days = max( 1, min( 365, (int) Settings_Manager::get( 'coupons', 'cookie_days', 7 ) ) );

		$base = array(
			'bg_color'                => $bg,
			'gradient_to'             => $bg,
			'text_color'              => $text,
			'name_color'              => $text,
			'marketing_color'         => $text,
			'cta_bg_color'            => '#FFFFFF',
			'cta_text_color'          => $bg,
			'cta_code_color'          => '',
			'cta_icon_color'          => '',
			'value_enabled'           => true,
			'cta_enabled'             => true,
			'cta_label'               => __( 'Copy coupon', 'suitemkt3k' ),
			'cta_optin_label'         => __( 'Request your coupon', 'suitemkt3k' ),
			'cta_step2_label'         => __( 'Receive', 'suitemkt3k' ),
			'cta_radius'              => '10',
			'popup_radius'            => '18',
			'image_id'                => 0,
			'image_url'               => '',
			'image_opacity'           => 1,
			'banner_enabled'          => true,
			'popup_enabled'           => true,
			'popup_delay'             => $popup_delay,
			'position'                => 'top',
			'devices'                 => array( 'desktop' => true, 'mobile' => true ),
			'product_id'              => 0,
			'form_fields'             => array(
				array( 'key' => 'name', 'label' => __( 'Name', 'suitemkt3k' ), 'required' => true, 'enabled' => true ),
				array( 'key' => 'email', 'label' => __( 'Email', 'suitemkt3k' ), 'required' => true, 'enabled' => true ),
				array( 'key' => 'birth_date', 'label' => __( 'Birth date', 'suitemkt3k' ), 'required' => false, 'enabled' => true ),
			),
			'field_label_color'       => $text,
			'field_placeholder_color' => $text,
			'field_text_color'        => $text,
			'field_bg_color'          => '#FFFFFF',
			'field_bg_opacity'        => 0.14,
			'field_border_enabled'    => true,
			'field_border_color'      => $text,
			'field_radius'            => 8,
			'consent_text'            => __( 'I agree to receive marketing emails and discounts.', 'suitemkt3k' ),
			'consent_text_color'      => $text,
			'success_message'         => __( 'Thanks! Check your inbox for your coupon.', 'suitemkt3k' ),
			'cookie_days'             => $cookie_days,
		);
		return array_replace_recursive( $base, $overrides );
	}

	/**
	 * Available currencies (used by both backend and the designer dropdown).
	 */
	public static function currency_options(): array {
		return array(
			'eur' => array( 'label' => __( 'Euro (€)', 'suitemkt3k' ), 'symbol' => '€', 'code' => 'EUR' ),
			'usd' => array( 'label' => __( 'US Dollar ($)', 'suitemkt3k' ), 'symbol' => '$', 'code' => 'USD' ),
			'gbp' => array( 'label' => __( 'British Pound (£)', 'suitemkt3k' ), 'symbol' => '£', 'code' => 'GBP' ),
			'jpy' => array( 'label' => __( 'Japanese Yen (¥)', 'suitemkt3k' ), 'symbol' => '¥', 'code' => 'JPY' ),
		);
	}

	/**
	 * Sanitize and persist a coupon campaign. Returns the row.
	 *
	 * @param array $data
	 * @param int   $id 0 to create.
	 * @return array
	 */
	public static function save( array $data, int $id = 0 ): array {
		global $wpdb;

		$name = Sanitizer::text( $data['name'] ?? '', 190 );
		if ( '' === $name ) {
			$name = sprintf( '%s %s', __( 'Coupon', 'suitemkt3k' ), gmdate( 'Y-m-d H:i' ) );
		}

		$format = Sanitizer::slug( $data['format_type'] ?? self::FORMAT_CLASSIC );
		if ( ! Validator::is_in( $format, array( self::FORMAT_CLASSIC, self::FORMAT_OPTIN ) ) ) {
			$format = self::FORMAT_CLASSIC;
		}

		$discount_type = Sanitizer::slug( $data['discount_type'] ?? 'percent' );
		$allowed_types = array_merge( array( 'percent', 'fixed' ), array_keys( self::currency_options() ) );
		if ( ! Validator::is_in( $discount_type, $allowed_types ) ) {
			$discount_type = 'percent';
		}

		$raw_value      = $data['discount_value'] ?? 0;
		$discount_value = Sanitizer::float( $raw_value, 0, 1000000 );
		if ( 'percent' === $discount_type ) {
			// Percent discounts are integers.
			$discount_value = (float) (int) round( $discount_value );
		} else {
			// Currency discounts: trim to at most 2 decimals (and keep them only if the user typed them).
			$discount_value = (float) sprintf( '%.2f', $discount_value );
		}

		$status = Sanitizer::slug( $data['status'] ?? self::STATUS_DRAFT );
		if ( ! Validator::is_in( $status, array(
			self::STATUS_DRAFT,
			self::STATUS_SCHEDULED,
			self::STATUS_ACTIVE,
			self::STATUS_PAUSED,
			self::STATUS_EXPIRED,
		) ) ) {
			$status = self::STATUS_DRAFT;
		}

		$row = array(
			'name'           => $name,
			'marketing_text' => Sanitizer::textarea( $data['marketing_text'] ?? '', 1000 ),
			'format_type'    => $format,
			'discount_type'  => $discount_type,
			'discount_value' => $discount_value,
			'usage_limit'    => isset( $data['usage_limit'] ) && '' !== $data['usage_limit'] ? Sanitizer::int( $data['usage_limit'], 0 ) : null,
			'status'         => $status,
			'starts_at'      => Sanitizer::datetime( $data['starts_at'] ?? '' ),
			'ends_at'        => Sanitizer::datetime( $data['ends_at'] ?? '' ),
		);

		$design_input = isset( $data['design'] ) && is_array( $data['design'] ) ? Sanitizer::recursive_text( $data['design'] ) : array();
		$design       = self::default_design( $design_input );
		if ( self::FORMAT_OPTIN === $format ) {
			// Opt-in step 1 CTA is required to reach the signup form.
			$design['cta_enabled'] = true;
		}

		if ( $id > 0 ) {
			$wpdb->update( self::table(), $row, array( 'id' => $id ) );
		} else {
			$wpdb->insert( self::table(), $row );
			$id = (int) $wpdb->insert_id;
		}

		$wpdb->replace(
			self::design_table(),
			array(
				'coupon_id'   => $id,
				'design_json' => wp_json_encode( $design ),
			),
			array( '%d', '%s' )
		);

		self::sync_wc_coupon( $id );

		Logger::info( 'coupons', 'Saved campaign', array( 'id' => $id, 'status' => $status ) );

		return (array) self::get( $id );
	}

	public static function delete( int $id ): bool {
		global $wpdb;
		$existing = self::get( $id );
		if ( ! $existing ) {
			return false;
		}
		if ( ! empty( $existing['wc_coupon_id'] ) && WooCommerce_Check::is_active() ) {
			wp_delete_post( (int) $existing['wc_coupon_id'], true );
		}
		$wpdb->delete( self::design_table(), array( 'coupon_id' => $id ), array( '%d' ) );
		return false !== $wpdb->delete( self::table(), array( 'id' => $id ), array( '%d' ) );
	}

	public static function duplicate( int $id ): ?array {
		$src = self::get( $id );
		if ( ! $src ) {
			return null;
		}
		$src['name']   .= ' ' . __( '(copy)', 'suitemkt3k' );
		$src['status']  = self::STATUS_DRAFT;
		unset( $src['id'], $src['wc_coupon_id'], $src['created_at'], $src['updated_at'] );
		return self::save( $src, 0 );
	}

	/**
	 * WooCommerce coupon code shown on classic CTAs (copy button).
	 */
	public static function get_coupon_code( array $coupon ): string {
		$wc_id = (int) ( $coupon['wc_coupon_id'] ?? 0 );
		if ( $wc_id > 0 ) {
			$post = get_post( $wc_id );
			if ( $post && 'shop_coupon' === $post->post_type ) {
				return (string) $post->post_title;
			}
		}
		$code = sanitize_text_field( (string) ( $coupon['name'] ?? '' ) );
		$code = preg_replace( '/\s+/', '-', strtoupper( $code ) );
		$code = preg_replace( '/[^A-Z0-9\-]/', '', (string) $code );
		return '' !== $code ? $code : 'WMK-' . (int) ( $coupon['id'] ?? 0 );
	}

	/**
	 * Create or update a WooCommerce shop_coupon based on the campaign.
	 */
	public static function sync_wc_coupon( int $id ): void {
		global $wpdb;

		if ( ! WooCommerce_Check::is_active() ) {
			return;
		}
		$row = self::get( $id );
		if ( ! $row ) {
			return;
		}

		$code = sanitize_text_field( $row['name'] );
		$code = preg_replace( '/\s+/', '-', strtoupper( $code ) );
		$code = preg_replace( '/[^A-Z0-9\-]/', '', (string) $code );
		if ( '' === $code ) {
			$code = 'WMK-' . $id;
		}

		$post_id = (int) $row['wc_coupon_id'];
		if ( $post_id && get_post( $post_id ) ) {
			wp_update_post(
				array(
					'ID'           => $post_id,
					'post_title'   => $code,
					'post_status'  => self::STATUS_ACTIVE === $row['status'] ? 'publish' : 'draft',
				)
			);
		} else {
			$post_id = wp_insert_post(
				array(
					'post_title'  => $code,
					'post_status' => self::STATUS_ACTIVE === $row['status'] ? 'publish' : 'draft',
					'post_type'   => 'shop_coupon',
					'post_author' => get_current_user_id() ?: 1,
				)
			);
			if ( ! is_wp_error( $post_id ) && $post_id > 0 ) {
				$wpdb->update( self::table(), array( 'wc_coupon_id' => (int) $post_id ), array( 'id' => $id ), array( '%d' ), array( '%d' ) );
			}
		}
		if ( ! $post_id || is_wp_error( $post_id ) ) {
			return;
		}
		$discount_type = 'percent' === $row['discount_type'] ? 'percent' : 'fixed_cart';
		update_post_meta( $post_id, 'discount_type', $discount_type );
		update_post_meta( $post_id, 'coupon_amount', self::format_amount( (float) $row['discount_value'], (string) $row['discount_type'] ) );
		update_post_meta( $post_id, 'usage_limit', isset( $row['usage_limit'] ) ? (string) (int) $row['usage_limit'] : '' );
		if ( ! empty( $row['ends_at'] ) ) {
			update_post_meta( $post_id, 'date_expires', (string) strtotime( $row['ends_at'] ) );
		}

		// Optional product restriction (single product ID configured in the designer).
		$product_id = isset( $row['design']['product_id'] ) ? (int) $row['design']['product_id'] : 0;
		if ( $product_id > 0 ) {
			update_post_meta( $post_id, 'product_ids', (string) $product_id );
		} else {
			delete_post_meta( $post_id, 'product_ids' );
		}

		update_post_meta( $post_id, '_suitemkt3k_campaign_id', $id );
	}

	public static function handle_save(): void {
		Capabilities::ensure( Capabilities::EDIT_CAMPAIGN );
		check_admin_referer( Nonces::ADMIN_ACTION, '_wpnonce' );
		$id_raw  = filter_input( INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT );
		$id      = max( 0, (int) $id_raw );
		$payload = Sanitizer::json_post( 'payload' );
		$row = self::save( $payload, $id );
		wp_safe_redirect( add_query_arg( array( 'view' => 'edit', 'id' => (int) $row['id'], 'updated' => '1' ), admin_url( 'admin.php?page=suitemkt3k-coupons' ) ) );
		exit;
	}

	public static function handle_delete(): void {
		Capabilities::ensure( Capabilities::EDIT_CAMPAIGN );
		check_admin_referer( Nonces::ADMIN_ACTION, '_wpnonce' );
		$id_raw = filter_input( INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT );
		$id     = max( 0, (int) $id_raw );
		if ( $id ) {
			self::delete( $id );
		}
		wp_safe_redirect( admin_url( 'admin.php?page=suitemkt3k-coupons&deleted=1' ) );
		exit;
	}

	public static function handle_duplicate(): void {
		Capabilities::ensure( Capabilities::EDIT_CAMPAIGN );
		check_admin_referer( Nonces::ADMIN_ACTION, '_wpnonce' );
		$id_raw = filter_input( INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT );
		$id     = max( 0, (int) $id_raw );
		$row = $id ? self::duplicate( $id ) : null;
		$target = $row ? add_query_arg( array( 'view' => 'edit', 'id' => (int) $row['id'] ), admin_url( 'admin.php?page=suitemkt3k-coupons' ) ) : admin_url( 'admin.php?page=suitemkt3k-coupons' );
		wp_safe_redirect( $target );
		exit;
	}

	/**
	 * Toggle a campaign between active and paused. Scheduled/expired remain untouched.
	 */
	public static function handle_toggle(): void {
		global $wpdb;
		Capabilities::ensure( Capabilities::EDIT_CAMPAIGN );
		check_admin_referer( Nonces::ADMIN_ACTION, '_wpnonce' );
		$id_raw = filter_input( INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT );
		$id     = max( 0, (int) $id_raw );
		$row = $id ? self::get( $id ) : null;
		if ( $row && in_array( $row['status'], array( self::STATUS_ACTIVE, self::STATUS_PAUSED, self::STATUS_DRAFT ), true ) ) {
			$new = self::STATUS_ACTIVE === $row['status'] ? self::STATUS_PAUSED : self::STATUS_ACTIVE;
			$wpdb->update( self::table(), array( 'status' => $new ), array( 'id' => $id ), array( '%s' ), array( '%d' ) );
			self::sync_wc_coupon( $id );
			Logger::info( 'coupons', 'Toggled status', array( 'id' => $id, 'status' => $new ) );
		}
		wp_safe_redirect( admin_url( 'admin.php?page=suitemkt3k-coupons' ) );
		exit;
	}

	public static function get_active_for_display(): array {
		global $wpdb;
		$now = current_time( 'mysql' );
		$rows = $wpdb->get_results(
			$wpdb->prepare(
				'SELECT c.*, d.design_json FROM %i c LEFT JOIN %i d ON d.coupon_id = c.id WHERE c.status = %s AND (c.starts_at IS NULL OR c.starts_at <= %s) AND (c.ends_at IS NULL OR c.ends_at >= %s) ORDER BY c.created_at DESC',
				self::table(),
				self::design_table(),
				self::STATUS_ACTIVE,
				$now,
				$now
			),
			ARRAY_A
		);
		return array_map( array( __CLASS__, 'inflate' ), (array) $rows );
	}
}
