<?php
/**
 * Parcel/member and parcel/tenant assignments.
 *
 * @package KGV\VereinManager
 */

namespace KGV\VereinManager\Repositories;

use KGV\VereinManager\Roles;
use KGV\VereinManager\Schema;

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

class AssignmentRepository {

	/**
	 * WordPress database object.
	 *
	 * @var \wpdb
	 */
	private $wpdb;

	/**
	 * Member assignment table.
	 *
	 * @var string
	 */
	private $member_table;

	/**
	 * Tenant assignment table.
	 *
	 * @var string
	 */
	private $tenant_table;

	/**
	 * Construct repository.
	 */
	public function __construct() {
		global $wpdb;

		$this->wpdb         = $wpdb;
		$this->member_table = Schema::table( 'parcel_members' );
		$this->tenant_table = Schema::table( 'parcel_tenants' );
	}

	/**
	 * Get WordPress users with the role "Mitglied".
	 *
	 * @return array
	 */
	public function get_member_users() {
		$query = new \WP_User_Query(
			array(
				'role'    => Roles::MEMBER_ROLE,
				'orderby' => 'display_name',
				'order'   => 'ASC',
				'number'  => 500,
			)
		);

		return $query->get_results();
	}

	/**
	 * Get assigned member IDs for a parcel.
	 *
	 * @param int $parcel_id Parcel ID.
	 * @return array
	 */
	public function get_member_ids_for_parcel( $parcel_id ) {
		$ids = $this->wpdb->get_col( $this->wpdb->prepare( "SELECT user_id FROM {$this->member_table} WHERE parcel_id = %d", $parcel_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
		return array_map( 'absint', $ids );
	}

	/**
	 * Get all members assigned to one parcel.
	 *
	 * @param int $parcel_id Parcel ID.
	 * @return array
	 */
	public function get_members_for_parcel( $parcel_id ) {
		$users = $this->wpdb->users;
		$sql   = "SELECT u.ID, u.display_name, u.user_email
			FROM {$this->member_table} pm
			INNER JOIN {$users} u ON u.ID = pm.user_id
			WHERE pm.parcel_id = %d
			ORDER BY u.display_name ASC";

		return $this->wpdb->get_results( $this->wpdb->prepare( $sql, $parcel_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
	}

	/**
	 * Get all parcels assigned to a specific user.
	 *
	 * @param int $user_id User ID.
	 * @return array
	 */
	public function get_parcels_for_user( $user_id ) {
		$parcels  = Schema::table( 'parcels' );
		$sections = Schema::table( 'sections' );
		$meters   = Schema::table( 'meters' );

		$sql = "SELECT p.*, s.name AS section_name,
			(SELECT meter_number FROM {$meters} wm WHERE wm.parcel_id = p.id AND wm.type = 'water' LIMIT 1) AS water_meter_number,
			(SELECT meter_number FROM {$meters} em WHERE em.parcel_id = p.id AND em.type = 'power' LIMIT 1) AS power_meter_number
			FROM {$this->member_table} pm
			INNER JOIN {$parcels} p ON p.id = pm.parcel_id
			LEFT JOIN {$sections} s ON s.id = p.section_id
			WHERE pm.user_id = %d
			ORDER BY s.name ASC, p.label ASC";

		return $this->wpdb->get_results( $this->wpdb->prepare( $sql, $user_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
	}

	/**
	 * Check whether a parcel belongs to a given user.
	 *
	 * @param int $user_id   User ID.
	 * @param int $parcel_id Parcel ID.
	 * @return bool
	 */
	public function user_has_parcel( $user_id, $parcel_id ) {
		$sql = "SELECT COUNT(*) FROM {$this->member_table} WHERE user_id = %d AND parcel_id = %d";
		return (int) $this->wpdb->get_var( $this->wpdb->prepare( $sql, $user_id, $parcel_id ) ) > 0; // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
	}

	/**
	 * Get assigned tenant IDs for a parcel.
	 *
	 * @param int $parcel_id Parcel ID.
	 * @return array
	 */
	public function get_tenant_ids_for_parcel( $parcel_id ) {
		$ids = $this->wpdb->get_col( $this->wpdb->prepare( "SELECT tenant_id FROM {$this->tenant_table} WHERE parcel_id = %d", $parcel_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
		return array_map( 'absint', $ids );
	}

	/**
	 * Get all members linked to a tenant via assigned parcels.
	 *
	 * @param int $tenant_id Tenant ID.
	 * @return array
	 */
	public function get_members_for_tenant( $tenant_id ) {
		$parcels = Schema::table( 'parcels' );
		$users   = $this->wpdb->users;
		$sql     = "SELECT DISTINCT u.ID, u.display_name, u.user_email, p.label AS parcel_label
			FROM {$this->tenant_table} pt
			INNER JOIN {$parcels} p ON p.id = pt.parcel_id
			INNER JOIN {$this->member_table} pm ON pm.parcel_id = pt.parcel_id
			INNER JOIN {$users} u ON u.ID = pm.user_id
			WHERE pt.tenant_id = %d
			ORDER BY p.label ASC, u.display_name ASC";

		return $this->wpdb->get_results( $this->wpdb->prepare( $sql, $tenant_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
	}

	/**
	 * Get all tenants assigned to one parcel.
	 *
	 * @param int $parcel_id Parcel ID.
	 * @return array
	 */
	public function get_tenants_for_parcel( $parcel_id ) {
		$tenants = Schema::table( 'tenants' );
		$sql     = "SELECT t.*
			FROM {$this->tenant_table} pt
			INNER JOIN {$tenants} t ON t.id = pt.tenant_id
			WHERE pt.parcel_id = %d
			ORDER BY t.last_name ASC, t.first_name ASC";

		return $this->wpdb->get_results( $this->wpdb->prepare( $sql, $parcel_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
	}

	/**
	 * Get all parcels assigned to one tenant.
	 *
	 * @param int $tenant_id Tenant ID.
	 * @return array
	 */
	public function get_parcels_for_tenant( $tenant_id ) {
		$parcels  = Schema::table( 'parcels' );
		$sections = Schema::table( 'sections' );
		$sql      = "SELECT p.*, s.name AS section_name
			FROM {$this->tenant_table} pt
			INNER JOIN {$parcels} p ON p.id = pt.parcel_id
			LEFT JOIN {$sections} s ON s.id = p.section_id
			WHERE pt.tenant_id = %d
			ORDER BY s.name ASC, p.label ASC";

		return $this->wpdb->get_results( $this->wpdb->prepare( $sql, $tenant_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
	}

	/**
	 * Synchronize member assignments.
	 *
	 * @param int   $parcel_id Parcel ID.
	 * @param array $user_ids  User IDs.
	 * @return void
	 */
	public function sync_member_ids( $parcel_id, $user_ids ) {
		$user_ids = array_values( array_unique( array_filter( array_map( 'absint', (array) $user_ids ) ) ) );
		$this->wpdb->delete( $this->member_table, array( 'parcel_id' => $parcel_id ), array( '%d' ) );

		foreach ( $user_ids as $user_id ) {
			$this->wpdb->insert(
				$this->member_table,
				array(
					'parcel_id'  => $parcel_id,
					'user_id'    => $user_id,
					'created_at' => current_time( 'mysql' ),
				),
				array( '%d', '%d', '%s' )
			);
		}
	}

	/**
	 * Synchronize tenant assignments.
	 *
	 * @param int   $parcel_id  Parcel ID.
	 * @param array $tenant_ids Tenant IDs.
	 * @return void
	 */
	public function sync_tenant_ids( $parcel_id, $tenant_ids ) {
		$tenant_ids = array_values( array_unique( array_filter( array_map( 'absint', (array) $tenant_ids ) ) ) );
		$this->wpdb->delete( $this->tenant_table, array( 'parcel_id' => $parcel_id ), array( '%d' ) );

		foreach ( $tenant_ids as $tenant_id ) {
			$this->wpdb->insert(
				$this->tenant_table,
				array(
					'parcel_id'  => $parcel_id,
					'tenant_id'  => $tenant_id,
					'created_at' => current_time( 'mysql' ),
				),
				array( '%d', '%d', '%s' )
			);
		}
	}

	/**
	 * Check whether a member is assigned to a different parcel.
	 *
	 * @param int $user_id   User ID.
	 * @param int $parcel_id Current parcel ID.
	 * @return bool
	 */
	public function member_has_other_parcels( $user_id, $parcel_id ) {
		$sql = "SELECT COUNT(*) FROM {$this->member_table} WHERE user_id = %d AND parcel_id != %d";
		return (int) $this->wpdb->get_var( $this->wpdb->prepare( $sql, $user_id, $parcel_id ) ) > 0; // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
	}

	/**
	 * Remove all assignments for a parcel.
	 *
	 * @param int $parcel_id Parcel ID.
	 * @return void
	 */
	public function purge_parcel( $parcel_id ) {
		$this->wpdb->delete( $this->member_table, array( 'parcel_id' => $parcel_id ), array( '%d' ) );
		$this->wpdb->delete( $this->tenant_table, array( 'parcel_id' => $parcel_id ), array( '%d' ) );
	}

	/**
	 * Remove all tenant relations before deleting the tenant.
	 *
	 * @param int $tenant_id Tenant ID.
	 * @return void
	 */
	public function purge_tenant( $tenant_id ) {
		$this->wpdb->delete( $this->tenant_table, array( 'tenant_id' => $tenant_id ), array( '%d' ) );
	}
}
