<?php
/**
 * Annual membership fee renewals (per calendar year).
 *
 * @package TonItaliaRegistration
 */

defined( 'ABSPATH' ) || exit;

/**
 * Class TON_Reg_Membership_Renewals
 */
class TON_Reg_Membership_Renewals {

	const STATUS_PENDING = 'pending';
	const STATUS_PAID    = 'paid';

	/**
	 * @return string
	 */
	public static function table_name() {
		global $wpdb;
		return $wpdb->prefix . 'ton_reg_membership_renewals';
	}

	/**
	 * Create renewals table.
	 */
	public static function create_table() {
		global $wpdb;

		$table   = self::table_name();
		$charset = $wpdb->get_charset_collate();

		$sql = "CREATE TABLE {$table} (
			id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
			registration_id bigint(20) unsigned NOT NULL,
			year smallint(5) unsigned NOT NULL,
			status varchar(20) NOT NULL DEFAULT 'pending',
			paid_at datetime DEFAULT NULL,
			paid_by bigint(20) unsigned DEFAULT NULL,
			reminder_first_jan_sent_at datetime DEFAULT NULL,
			reminder_last_jan_sent_at datetime DEFAULT NULL,
			created_at datetime NOT NULL,
			updated_at datetime NOT NULL,
			PRIMARY KEY  (id),
			UNIQUE KEY registration_year (registration_id, year),
			KEY year (year),
			KEY status (status)
		) {$charset};";

		require_once ABSPATH . 'wp-admin/includes/upgrade.php';
		dbDelta( $sql );
	}

	/**
	 * Current calendar year (site timezone).
	 *
	 * @return int
	 */
	public static function current_year() {
		return (int) wp_date( 'Y' );
	}

	/**
	 * @param object $registration Registration row.
	 * @return int
	 */
	public static function admission_year( $registration ) {
		if ( ! empty( $registration->libro_associati_date ) ) {
			$timestamp = strtotime( (string) $registration->libro_associati_date );
			if ( $timestamp ) {
				return (int) wp_date( 'Y', $timestamp );
			}
		}

		if ( ! empty( $registration->created_at ) ) {
			$timestamp = strtotime( (string) $registration->created_at );
			if ( $timestamp ) {
				return (int) wp_date( 'Y', $timestamp );
			}
		}

		return self::current_year();
	}

	/**
	 * Years shown in admin checkboxes: from admission through current calendar year only.
	 *
	 * @param object $registration Registration row.
	 * @return array{start:int,end:int}
	 */
	public static function get_checkbox_year_range( $registration ) {
		$start = self::admission_year( $registration );
		$end   = self::current_year();

		if ( $start > $end ) {
			$start = $end;
		}

		return array(
			'start' => $start,
			'end'   => $end,
		);
	}

	/**
	 * @param int $registration_id Registration ID.
	 * @param int $year            Calendar year.
	 * @return string paid|pending
	 */
	public static function get_status( $registration_id, $year ) {
		$row = self::get_by_registration_year( $registration_id, $year );
		if ( ! $row || self::STATUS_PAID !== (string) $row->status ) {
			return self::STATUS_PENDING;
		}

		return self::STATUS_PAID;
	}

	/**
	 * @param int $registration_id Registration ID.
	 * @param int $year            Calendar year.
	 * @return object|null
	 */
	public static function get_by_registration_year( $registration_id, $year ) {
		global $wpdb;

		return $wpdb->get_row(
			$wpdb->prepare(
				'SELECT * FROM ' . self::table_name() . ' WHERE registration_id = %d AND year = %d',
				$registration_id,
				$year
			)
		);
	}

	/**
	 * @param int $registration_id Registration ID.
	 * @return array<int,object>
	 */
	public static function get_by_registration( $registration_id ) {
		global $wpdb;

		return $wpdb->get_results(
			$wpdb->prepare(
				'SELECT * FROM ' . self::table_name() . ' WHERE registration_id = %d ORDER BY year ASC',
				$registration_id
			)
		);
	}

	/**
	 * @param array<int> $registration_ids Registration IDs.
	 * @param int        $year             Calendar year.
	 * @return array<int,string> Map registration_id => status.
	 */
	public static function get_statuses_for_registrations( array $registration_ids, $year ) {
		global $wpdb;

		$registration_ids = array_values( array_filter( array_map( 'intval', $registration_ids ) ) );
		if ( empty( $registration_ids ) ) {
			return array();
		}

		$placeholders = implode( ',', array_fill( 0, count( $registration_ids ), '%d' ) );
		$params       = array_merge( array( (int) $year ), $registration_ids );

		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
		$sql = $wpdb->prepare(
			'SELECT registration_id, status FROM ' . self::table_name() . " WHERE year = %d AND registration_id IN ({$placeholders})",
			$params
		);

		$rows = $wpdb->get_results( $sql );
		$map  = array();

		foreach ( $registration_ids as $id ) {
			$map[ $id ] = self::STATUS_PENDING;
		}

		foreach ( $rows as $row ) {
			$map[ (int) $row->registration_id ] = (string) $row->status;
		}

		return $map;
	}

	/**
	 * @param int  $registration_id Registration ID.
	 * @param int  $year            Calendar year.
	 * @param bool $paid            Whether fee is paid.
	 * @param int  $user_id         Admin user marking payment.
	 * @return bool
	 */
	public static function set_paid( $registration_id, $year, $paid, $user_id = 0 ) {
		global $wpdb;

		$registration_id = (int) $registration_id;
		$year            = (int) $year;
		$user_id         = (int) $user_id;
		$now             = current_time( 'mysql', true );
		$existing        = self::get_by_registration_year( $registration_id, $year );

		$data = array(
			'status'     => $paid ? self::STATUS_PAID : self::STATUS_PENDING,
			'paid_at'    => $paid ? $now : null,
			'paid_by'    => $paid ? ( $user_id ?: null ) : null,
			'updated_at' => $now,
		);

		if ( $existing ) {
			return false !== $wpdb->update(
				self::table_name(),
				$data,
				array( 'id' => (int) $existing->id ),
				null,
				array( '%d' )
			);
		}

		$data['registration_id'] = $registration_id;
		$data['year']            = $year;
		$data['created_at']      = $now;

		return false !== $wpdb->insert( self::table_name(), $data );
	}

	/**
	 * Ensure a pending row exists for reminder tracking.
	 *
	 * @param int $registration_id Registration ID.
	 * @param int $year            Calendar year.
	 * @return object|null
	 */
	public static function ensure_pending_row( $registration_id, $year ) {
		$existing = self::get_by_registration_year( $registration_id, $year );
		if ( $existing ) {
			return $existing;
		}

		$now = current_time( 'mysql', true );
		global $wpdb;

		$wpdb->insert(
			self::table_name(),
			array(
				'registration_id' => (int) $registration_id,
				'year'            => (int) $year,
				'status'          => self::STATUS_PENDING,
				'created_at'      => $now,
				'updated_at'      => $now,
			)
		);

		return self::get_by_registration_year( $registration_id, $year );
	}

	/**
	 * @param int    $registration_id Registration ID.
	 * @param int    $year            Calendar year.
	 * @param string $reminder_type   first|last.
	 * @return bool
	 */
	public static function mark_reminder_sent( $registration_id, $year, $reminder_type ) {
		global $wpdb;

		$row = self::ensure_pending_row( $registration_id, $year );
		if ( ! $row ) {
			return false;
		}

		$column = 'first' === $reminder_type ? 'reminder_first_jan_sent_at' : 'reminder_last_jan_sent_at';
		$now    = current_time( 'mysql', true );

		return false !== $wpdb->update(
			self::table_name(),
			array(
				$column      => $now,
				'updated_at' => $now,
			),
			array( 'id' => (int) $row->id ),
			null,
			array( '%d' )
		);
	}

	/**
	 * Admitted members without paid renewal for a given year.
	 *
	 * @param int    $year          Calendar year.
	 * @param string $reminder_type first|last — skip if that reminder was already sent.
	 * @return array<int,object> Registration rows.
	 */
	public static function get_unpaid_admitted( $year, $reminder_type = '' ) {
		global $wpdb;

		$reg_table = TON_Reg_Database::table_name();
		$ren_table = self::table_name();
		$year      = (int) $year;

		$sql = "SELECT r.* FROM {$reg_table} r
			LEFT JOIN {$ren_table} ren ON ren.registration_id = r.id AND ren.year = %d
			WHERE r.status = 'admitted'
			AND r.anonymized_at IS NULL
			AND r.email <> ''
			AND ( ren.id IS NULL OR ren.status <> %s )";

		$params = array( $year, self::STATUS_PAID );

		if ( 'first' === $reminder_type ) {
			$sql     .= ' AND ( ren.reminder_first_jan_sent_at IS NULL )';
		} elseif ( 'last' === $reminder_type ) {
			$sql     .= ' AND ( ren.reminder_last_jan_sent_at IS NULL )';
		}

		$sql .= ' ORDER BY r.cognome ASC, r.nome ASC, r.id ASC';

		return $wpdb->get_results( $wpdb->prepare( $sql, $params ) );
	}

	/**
	 * Registration IDs with pending renewal for a year.
	 *
	 * @param int $year Calendar year.
	 * @return array<int>
	 */
	public static function get_unpaid_registration_ids( $year ) {
		$rows = self::get_unpaid_admitted( $year );
		return array_map(
			static function ( $row ) {
				return (int) $row->id;
			},
			$rows
		);
	}

	/**
	 * Display label and CSS class for list table badge.
	 *
	 * @param string $status Renewal status (paid|pending).
	 * @param int    $year   Calendar year shown.
	 * @return array{label:string,class:string}
	 */
	public static function display_badge( $status, $year ) {
		$year = (int) $year;

		if ( self::STATUS_PAID === $status ) {
			return array(
				'label' => sprintf(
					/* translators: %d: calendar year */
					__( 'Pagato %d', 'ton-italia-registration' ),
					$year
				),
				'class' => 'ton-reg-renewal--paid',
			);
		}

		$month = (int) wp_date( 'n' );
		$day   = (int) wp_date( 'j' );

		if ( 1 === $month && $day >= 25 ) {
			return array(
				'label' => sprintf(
					/* translators: %d: calendar year */
					__( 'Sollecito %d', 'ton-italia-registration' ),
					$year
				),
				'class' => 'ton-reg-renewal--reminder',
			);
		}

		if ( $month >= 2 ) {
			return array(
				'label' => sprintf(
					/* translators: %d: calendar year */
					__( 'Non pagato %d', 'ton-italia-registration' ),
					$year
				),
				'class' => 'ton-reg-renewal--overdue',
			);
		}

		return array(
			'label' => sprintf(
				/* translators: %d: calendar year */
				__( 'Da pagare %d', 'ton-italia-registration' ),
				$year
			),
			'class' => 'ton-reg-renewal--pending',
		);
	}
}
