<?php
/**
 * Teacher front-end portal: session, login, and views.
 *
 * Mirrors TSM_Public for students, but for teachers. Teachers can:
 *   - log in with the username/password admins assign in the Teachers screen
 *   - view their assigned classes
 *   - drill into a class to see enrolled students and current grades
 *   - enter / update grades on assignments for their classes
 *   - mark attendance for their classes (Pro feature, always on in AIO)
 *
 * NOTE: Front-end self-registration is intentionally NOT supported. All
 * teacher and student accounts must be created from the WordPress admin
 * (TradeSchool > Teachers / Students). This file exposes no signup form.
 *
 * @package TradeSchoolManager
 */

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

class TSM_Teacher_Portal {

	const COOKIE = 'tsm_teacher_session';
	private static $instance = null;

	public static function instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	private function __construct() {
		add_action( 'init', array( $this, 'maybe_handle_logout' ) );
		add_action( 'init', array( $this, 'maybe_handle_csv_export' ) );
		add_action( 'init', array( $this, 'maybe_handle_login' ) );
		add_action( 'init', array( $this, 'maybe_handle_profile_update' ) );
		add_action( 'init', array( $this, 'maybe_handle_change_password' ) );
		add_action( 'init', array( $this, 'maybe_handle_action' ), 11 );
	}

	/**
	 * Handle teacher profile photo + email/phone updates from Settings.
	 */
	public function maybe_handle_profile_update() {
		if ( ! isset( $_POST['tsm_teacher_profile_nonce'] ) ) {
			return;
		}
		if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['tsm_teacher_profile_nonce'] ) ), 'tsm_teacher_profile' ) ) {
			return;
		}
		$tid = self::current_teacher_id();
		if ( ! $tid ) {
			return;
		}
		global $wpdb;
		$updates = array();
		if ( ! empty( $_FILES['photo_file']['name'] ) ) {
			require_once ABSPATH . 'wp-admin/includes/file.php';
			$uploaded = wp_handle_upload(
				$_FILES['photo_file'], // phpcs:ignore
				array(
					'test_form' => false,
					'mimes'     => array(
						'png'  => 'image/png',
						'jpg'  => 'image/jpeg',
						'jpeg' => 'image/jpeg',
						'webp' => 'image/webp',
					),
				)
			);
			if ( ! empty( $uploaded['url'] ) ) {
				$updates['photo_url'] = esc_url_raw( $uploaded['url'] );
			}
		}
		$email = isset( $_POST['tsm_email'] ) ? sanitize_email( wp_unslash( $_POST['tsm_email'] ) ) : '';
		$phone = isset( $_POST['tsm_phone'] ) ? sanitize_text_field( wp_unslash( $_POST['tsm_phone'] ) ) : '';
		$updates['email'] = '' === $email ? null : $email;
		$updates['phone'] = $phone;
		if ( ! empty( $updates ) ) {
			$wpdb->update( TSM_Teachers::table(), $updates, array( 'id' => $tid ) ); // phpcs:ignore
		}
		wp_safe_redirect( add_query_arg( array( 'tsm_view' => 'settings', 'sub' => 'info', 'tsm_info_saved' => 1 ), remove_query_arg( array( 'assignment_edit', 'tsm_pw_error', 'tsm_pw_done' ) ) ) );
		exit;
	}

	/**
	 * Logged-in teachers can change their password from Settings.
	 */
	public function maybe_handle_change_password() {
		if ( empty( $_POST['tsm_teacher_change_pw_nonce'] ) ) {
			return;
		}
		if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['tsm_teacher_change_pw_nonce'] ) ), 'tsm_teacher_change_pw' ) ) {
			return;
		}
		$tid = self::current_teacher_id();
		$return = add_query_arg( array( 'tsm_view' => 'settings', 'sub' => 'password' ), remove_query_arg( array( 'assignment_edit', 'tsm_info_saved', 'tsm_pw_error', 'tsm_pw_done' ) ) );
		if ( ! $tid ) {
			wp_safe_redirect( add_query_arg( 'tsm_pw_error', 'session', $return ) );
			exit;
		}
		$teacher = TSM_Teachers::get( $tid );
		// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$current = (string) wp_unslash( $_POST['tsm_current_password'] ?? '' );
		// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$pw1 = (string) wp_unslash( $_POST['tsm_new_password'] ?? '' );
		// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$pw2 = (string) wp_unslash( $_POST['tsm_new_password2'] ?? '' );
		if ( ! $teacher || empty( $teacher->password_hash ) || ! wp_check_password( $current, $teacher->password_hash ) ) {
			wp_safe_redirect( add_query_arg( 'tsm_pw_error', 'current', $return ) );
			exit;
		}
		if ( strlen( $pw1 ) < 8 ) {
			wp_safe_redirect( add_query_arg( 'tsm_pw_error', 'short', $return ) );
			exit;
		}
		if ( $pw1 !== $pw2 ) {
			wp_safe_redirect( add_query_arg( 'tsm_pw_error', 'mismatch', $return ) );
			exit;
		}
		global $wpdb;
		$wpdb->update( TSM_Teachers::table(), array( 'password_hash' => wp_hash_password( $pw1 ) ), array( 'id' => $tid ) ); // phpcs:ignore
		wp_safe_redirect( add_query_arg( 'tsm_pw_done', 1, $return ) );
		exit;
	}

	public function maybe_handle_logout() {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Logout link is a simple GET flag; clears local cookie only.
		if ( ! isset( $_GET['tsm_teacher_logout'] ) ) {
			return;
		}
		self::logout();
		$login_url = TSM_Public::get_page_url( 'teacher_login_page', remove_query_arg( array( 'assignment_edit', 'tsm_teacher_logout' ) ) );
		wp_safe_redirect( add_query_arg( 'tsm_t_logged_out', 1, $login_url ) );
		exit;
	}

	/* ---------------------------------------------------------------- *
	 * Session / auth
	 * ---------------------------------------------------------------- */

	public static function verify_login( $username, $password ) {
		global $wpdb;
		$t = TSM_Teachers::table();
		$row = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$t} WHERE username = %s", $username ) ); // phpcs:ignore
		if ( ! $row || empty( $row->password_hash ) ) {
			return false;
		}
		if ( wp_check_password( $password, $row->password_hash ) ) {
			return $row;
		}
		return false;
	}

	public static function set_current( $teacher_id ) {
		$token = wp_generate_password( 32, false, false );
		set_transient( 'tsm_teacher_session_' . $token, (int) $teacher_id, DAY_IN_SECONDS * 7 );
		setcookie( self::COOKIE, $token, time() + DAY_IN_SECONDS * 7, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true );
		$_COOKIE[ self::COOKIE ] = $token;
	}

	public static function current_teacher_id() {
		if ( empty( $_COOKIE[ self::COOKIE ] ) ) {
			return 0;
		}
		$token = sanitize_text_field( wp_unslash( $_COOKIE[ self::COOKIE ] ) );
		$id    = get_transient( 'tsm_teacher_session_' . $token );
		return $id ? (int) $id : 0;
	}

	public static function logout() {
		if ( ! empty( $_COOKIE[ self::COOKIE ] ) ) {
			delete_transient( 'tsm_teacher_session_' . sanitize_text_field( wp_unslash( $_COOKIE[ self::COOKIE ] ) ) );
		}
		setcookie( self::COOKIE, '', time() - 3600, COOKIEPATH, COOKIE_DOMAIN );
	}

	public function maybe_handle_login() {
		if ( ! isset( $_POST['tsm_teacher_login'] ) ) {
			return;
		}
		if ( ! isset( $_POST['tsm_teacher_login_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['tsm_teacher_login_nonce'] ) ), 'tsm_teacher_login' ) ) {
			return;
		}
		$user = sanitize_user( wp_unslash( $_POST['tsm_teacher_username'] ?? '' ) );
		// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Raw password verified against hash; sanitizing would corrupt.
		$pass = isset( $_POST['tsm_teacher_password'] ) ? (string) wp_unslash( $_POST['tsm_teacher_password'] ) : '';
		$t    = self::verify_login( $user, $pass );
		if ( $t ) {
			self::set_current( (int) $t->id );
			$portal_url = TSM_Public::get_page_url( 'teacher_portal_page', remove_query_arg( array( 'assignment_edit', 'tsm_t_login_error' ) ) );
			wp_safe_redirect( $portal_url );
			exit;
		}
		wp_safe_redirect( add_query_arg( 'tsm_t_login_error', 1 ) );
		exit;
	}

	/* ---------------------------------------------------------------- *
	 * POST handlers (grade entry / attendance)
	 * ---------------------------------------------------------------- */

	public function maybe_handle_action() {
		if ( empty( $_POST['tsm_teacher_action'] ) ) {
			return;
		}
		$tid = self::current_teacher_id();
		if ( ! $tid ) {
			return;
		}
		$action = sanitize_text_field( wp_unslash( $_POST['tsm_teacher_action'] ) );

		/**
		 * Let add-ons handle teacher portal actions before core class actions run.
		 *
		 * Add-on handlers should validate their own nonce and ownership checks, then
		 * redirect and exit when they process the action.
		 */
		// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Intentional public extension hook namespaced with the plugin slug.
		do_action( 'tradeschool_manager_teacher_portal_handle_action', $action, $tid );

		if ( 'save_grades' === $action ) {
			check_admin_referer( 'tsm_teacher_save_grades' );
			$assignment_id = absint( $_POST['assignment_id'] ?? 0 );
			$assignment    = TSM_Assignments::get( $assignment_id );
			if ( ! $assignment || ! self::teacher_owns_class( $tid, $assignment->class_id ) ) {
				return;
			}
			$class       = TSM_Classes::get( $assignment->class_id );
			$is_passfail = ( $class && 'pass_fail' === $class->grading_method );
			// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Each field sanitized individually below before save.
			$grades      = isset( $_POST['grades'] ) && is_array( $_POST['grades'] ) ? wp_unslash( $_POST['grades'] ) : array();
			foreach ( $grades as $sid => $row ) {
				$sid = absint( $sid );
				if ( ! $sid ) continue;
				if ( $is_passfail && isset( $row['pass_fail'] ) ) {
					// Legacy pass/fail dropdown path (kept for backward compat).
					$pf = sanitize_text_field( $row['pass_fail'] );
					$payload = array(
						'status'   => ( 'excused' === $pf ) ? 'excused' : ( ( 'pass' === $pf || 'fail' === $pf ) ? 'completed_ontime' : 'ungraded' ),
						'comments' => isset( $row['comments'] ) ? sanitize_textarea_field( $row['comments'] ) : '',
					);
					if ( 'pass' === $pf )      { $payload['points_earned'] = (float) ( $assignment->points_possible ?: 1 ); }
					elseif ( 'fail' === $pf )  { $payload['points_earned'] = 0; }
					TSM_Gradebook::save_grade( $assignment_id, $sid, $payload );
				} else {
					TSM_Gradebook::save_grade( $assignment_id, $sid, array(
						'status'         => isset( $row['status'] ) ? sanitize_key( $row['status'] ) : '',
						'points_earned'  => isset( $row['points_earned'] ) ? sanitize_text_field( $row['points_earned'] ) : '',
						'bonus'          => isset( $row['bonus'] ) ? sanitize_text_field( $row['bonus'] ) : '',
						'late_penalty'   => isset( $row['late_penalty'] ) ? sanitize_text_field( $row['late_penalty'] ) : '',
						'submitted_date' => isset( $row['submitted_date'] ) ? sanitize_text_field( $row['submitted_date'] ) : '',
						'comments'       => isset( $row['comments'] ) ? sanitize_textarea_field( $row['comments'] ) : '',
					) );
				}
			}
			wp_safe_redirect( add_query_arg( array( 'tsm_view' => 'assignment', 'aid' => $assignment_id, 'saved' => 1 ) ) );
			exit;
		}

		if ( 'save_attendance' === $action && class_exists( 'TSMP_Attendance' ) ) {
			check_admin_referer( 'tsm_teacher_save_attendance' );
			$class_id = absint( $_POST['class_id'] ?? 0 );
			$date     = sanitize_text_field( wp_unslash( $_POST['attendance_date'] ?? gmdate( 'Y-m-d' ) ) );
			if ( ! self::teacher_owns_class( $tid, $class_id ) ) {
				return;
			}
			// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Each status sanitized individually in the loop below.
			$marks = isset( $_POST['attendance'] ) && is_array( $_POST['attendance'] ) ? wp_unslash( $_POST['attendance'] ) : array();
			foreach ( $marks as $sid => $status ) {
				TSMP_Attendance::set( $class_id, absint( $sid ), $date, sanitize_text_field( $status ) );
			}
			wp_safe_redirect( add_query_arg( array( 'tsm_view' => 'attendance', 'cid' => $class_id, 'saved' => 1 ) ) );
			exit;
		}

		if ( 'create_assignment' === $action && class_exists( 'TSM_Assignments' ) ) {
			check_admin_referer( 'tsm_teacher_create_assignment' );
			$cid = absint( $_POST['class_id'] ?? 0 );
			if ( ! self::teacher_owns_class( $tid, $cid ) ) { return; }
			$class       = TSM_Classes::get( $cid );
			$is_passfail = ( $class && 'pass_fail' === $class->grading_method );
			$points      = $is_passfail ? 1 : floatval( $_POST['points_possible'] ?? 0 );
			TSM_Assignments::save( array(
				'class_id'        => $cid,
				'school_year_id'  => $class ? (int) $class->school_year_id : 0,
				'title'           => sanitize_text_field( wp_unslash( $_POST['title'] ?? '' ) ),
				'description'     => wp_kses_post( wp_unslash( $_POST['description'] ?? '' ) ),
				'category'        => $is_passfail ? '' : sanitize_text_field( wp_unslash( $_POST['category'] ?? '' ) ),
				'assigned_date'   => TSM_Date_Utils::normalize( sanitize_text_field( wp_unslash( $_POST['assigned_date'] ?? '' ) ) ),
				'due_date'        => TSM_Date_Utils::normalize( sanitize_text_field( wp_unslash( $_POST['due_date'] ?? '' ) ) ),
				'points_possible' => $points,
				'extra_credit'    => ( ! $is_passfail && ! empty( $_POST['extra_credit'] ) ) ? 1 : 0,
				'required'        => ! empty( $_POST['required'] ) ? 1 : 0,
			) );
			self::redirect_to_class_tab( $cid, 'assignments', array( 'composer_done' => 'assignment' ) );
		}

		if ( 'update_assignment' === $action && class_exists( 'TSM_Assignments' ) ) {
			check_admin_referer( 'tsm_teacher_update_assignment' );
			$posted_cid    = absint( $_POST['class_id'] ?? 0 );
			$assignment_id = absint( $_POST['assignment_id'] ?? 0 );
			$assignment    = $assignment_id ? TSM_Assignments::get( $assignment_id ) : null;
			$cid           = $assignment ? absint( $assignment->class_id ) : $posted_cid;

			if ( ! $assignment || ! self::teacher_owns_class( $tid, $cid ) ) {
				if ( $posted_cid && self::teacher_owns_class( $tid, $posted_cid ) ) {
					self::redirect_to_class_tab( $posted_cid, 'assignments', array( 'composer_done' => 'assignment_error' ) );
				}
				return;
			}

			$class       = TSM_Classes::get( $cid );
			$is_passfail = ( $class && 'pass_fail' === $class->grading_method );
			$points      = $is_passfail ? 1 : floatval( wp_unslash( $_POST['points_possible'] ?? 0 ) );
			$result      = TSM_Assignments::save( array(
				'class_id'        => $cid,
				'school_year_id'  => $class ? (int) $class->school_year_id : 0,
				'title'           => sanitize_text_field( wp_unslash( $_POST['title'] ?? '' ) ),
				'description'     => wp_kses_post( wp_unslash( $_POST['description'] ?? '' ) ),
				'category'        => $is_passfail ? '' : sanitize_text_field( wp_unslash( $_POST['category'] ?? '' ) ),
				'assigned_date'   => TSM_Date_Utils::normalize( sanitize_text_field( wp_unslash( $_POST['assigned_date'] ?? '' ) ) ),
				'due_date'        => TSM_Date_Utils::normalize( sanitize_text_field( wp_unslash( $_POST['due_date'] ?? '' ) ) ),
				'points_possible' => $points,
				'extra_credit'    => ( ! $is_passfail && ! empty( $_POST['extra_credit'] ) ) ? 1 : 0,
				'required'        => ! empty( $_POST['required'] ) ? 1 : 0,
			), $assignment_id );

			self::redirect_to_class_tab( $cid, 'assignments', array( 'composer_done' => is_wp_error( $result ) ? 'assignment_error' : 'assignment_updated' ) );
		}

		if ( 'post_announcement' === $action && class_exists( 'TSM_Announcements' ) ) {
			$cid = absint( $_POST['class_id'] ?? 0 );
			if (
				empty( $_POST['tsm_teacher_ann_nonce'] )
				|| ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['tsm_teacher_ann_nonce'] ) ), 'tsm_teacher_post_announcement' )
			) {
				self::redirect_to_class_tab( $cid, 'announcements', array( 'composer_done' => 'announcement_error' ) );
			}
			if ( ! self::teacher_owns_class( $tid, $cid ) ) {
				self::redirect_to_class_tab( $cid, 'announcements', array( 'composer_done' => 'announcement_error' ) );
			}
			$id = absint( $_POST['announcement_id'] ?? 0 );
			if ( $id && ! self::teacher_can_manage_announcement( $tid, $id, $cid ) ) {
				self::redirect_to_class_tab( $cid, 'announcements', array( 'composer_done' => 'announcement_error' ) );
			}
			TSM_Announcements::save_for_class( $cid, array(
				'title'          => sanitize_text_field( wp_unslash( $_POST['title'] ?? '' ) ),
				'body'           => wp_kses_post( wp_unslash( $_POST['body'] ?? '' ) ),
				'end_date'       => TSM_Date_Utils::normalize( sanitize_text_field( wp_unslash( $_POST['end_date'] ?? '' ) ) ),
				'active'         => ! empty( $_POST['active'] ) ? 1 : 0,
			), $id );
			self::redirect_to_class_tab( $cid, 'announcements', array( 'composer_done' => $id ? 'announcement_updated' : 'announcement' ) );
		}

		if ( 'delete_announcement' === $action && class_exists( 'TSM_Announcements' ) ) {
			$cid = absint( $_POST['class_id'] ?? 0 );
			$id  = absint( $_POST['announcement_id'] ?? 0 );
			if (
				empty( $_POST['tsm_teacher_ann_delete_nonce'] )
				|| ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['tsm_teacher_ann_delete_nonce'] ) ), 'tsm_teacher_delete_announcement' )
			) {
				self::redirect_to_class_tab( $cid, 'announcements', array( 'composer_done' => 'announcement_error' ) );
			}
			if ( self::teacher_can_manage_announcement( $tid, $id, $cid ) ) {
				TSM_Announcements::delete( $id );
			} else {
				self::redirect_to_class_tab( $cid, 'announcements', array( 'composer_done' => 'announcement_error' ) );
			}
			self::redirect_to_class_tab( $cid, 'announcements', array( 'composer_done' => 'announcement_deleted' ) );
		}
	}

	private static function redirect_to_class_tab( $class_id, $tab, $extra = array() ) {
		$url = TSM_Public::get_page_url( 'teacher_portal_page', home_url( '/' ) );
		$args = array_merge(
			array(
				'tsm_view' => 'class',
				'cid'      => absint( $class_id ),
				'ctab'     => sanitize_key( $tab ),
			),
			$extra
		);
		$redirect = add_query_arg( $args, $url );
		if ( $class_id ) {
			$redirect .= '#tsm-teacher-class-' . absint( $class_id );
		}
		wp_safe_redirect( $redirect );
		exit;
	}

	private static function teacher_can_manage_announcement( $teacher_id, $announcement_id, $class_id ) {
		if ( ! class_exists( 'TSM_Announcements' ) || ! self::teacher_owns_class( $teacher_id, $class_id ) ) {
			return false;
		}
		$announcement = TSM_Announcements::get( $announcement_id );
		return $announcement
			&& 'class' === $announcement->audience_type
			&& absint( $announcement->audience_value ) === absint( $class_id );
	}

	private static function escape_csv_cell( $value ) {
		if ( is_scalar( $value ) || null === $value ) {
			$value = (string) $value;
			if ( '' !== $value && preg_match( '/^[=\-+@]/', $value ) ) {
				return "'" . $value;
			}
		}
		return $value;
	}

	private static function putcsv( $handle, $row ) {
		fputcsv( $handle, array_map( array( __CLASS__, 'escape_csv_cell' ), (array) $row ) );
	}

	/** GET handler: streams CSV roster (name, email, grade). */
	public function maybe_handle_csv_export() {
		if ( empty( $_GET['tsm_teacher_action'] ) || 'export_roster' !== $_GET['tsm_teacher_action'] ) { return; } // phpcs:ignore
		$tid = self::current_teacher_id();
		if ( ! $tid ) { return; }
		check_admin_referer( 'tsm_teacher_export_roster' );
		$cid = absint( $_GET['cid'] ?? 0 ); // phpcs:ignore
		if ( ! self::teacher_owns_class( $tid, $cid ) ) { wp_die( esc_html__( 'Not allowed.', 'tradeschool-manager' ) ); }
		$class    = TSM_Classes::get( $cid );
		$students = TSM_Classes::students_in_class( $cid );
		nocache_headers();
		header( 'Content-Type: text/csv; charset=utf-8' );
		header( 'Content-Disposition: attachment; filename="roster-' . sanitize_file_name( $class->name ) . '-' . gmdate( 'Y-m-d' ) . '.csv"' );
		$out = fopen( 'php://output', 'w' );
		self::putcsv( $out, array( 'Last Name', 'First Name', 'Email', 'Current %', 'Letter' ) );
		foreach ( $students as $s ) {
			$g = TSM_Gradebook::calc_class_grade( $cid, $s->id );
			self::putcsv( $out, array( $s->last_name, $s->first_name, $s->email, $g['percent'], $g['letter'] ) );
		}
		// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
		fclose( $out );
		exit;
	}

	public static function teacher_owns_class( $teacher_id, $class_id ) {
		return TSM_Classes::is_teacher_of( $class_id, $teacher_id );
	}
}
