<?php
/**
 * Health check service
 *
 * Inspects the site's actual schema configuration and content, and reports
 * findings as actionable checks. The dashboard renders these as its task
 * list — checks are data-driven and renewable, so the list stays truthful
 * after initial setup (new gaps surface as new findings).
 *
 * Each check returns:
 *   id        string  Stable identifier
 *   passing   bool    Whether the check currently passes
 *   title     string  Action-oriented title (what to do / what is done)
 *   detail    string  Specifics — which fields/items are missing
 *   priority  string  high | medium | low
 *   effort    string  Rough time to fix (e.g. "5m")
 *   link      string  Admin URL where the fix happens
 *   progress  string  Optional "x/y" completion counter ('' if n/a)
 *
 * @package BW_Schema
 * @since 3.1.0
 */

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

class BW_Schema_Service_Health {

	/**
	 * Run all health checks
	 *
	 * @return array[] List of check result arrays (see file docblock)
	 */
	public static function run_checks() {
		$checks = array(
			self::check_setup_complete(),
			self::check_organization_profile(),
			self::check_business_type(),
			self::check_team_members(),
			self::check_team_photos(),
			self::check_team_job_titles(),
			self::check_locations(),
			self::check_schema_conflicts(),
		);

		// Drop checks that returned null (not applicable in current config)
		return array_values( array_filter( $checks ) );
	}

	/**
	 * Summarize check results
	 *
	 * @return array { passing: int, total: int, percentage: float }
	 */
	public static function get_summary() {
		$checks  = self::run_checks();
		$total   = count( $checks );
		$passing = count( array_filter( $checks, function ( $c ) {
			return $c['passing'];
		} ) );

		return array(
			'passing'    => $passing,
			'total'      => $total,
			'percentage' => $total > 0 ? ( $passing / $total ) * 100 : 100,
		);
	}

	/**
	 * Check: first-time configuration completed
	 *
	 * Passes when the wizard was finished, or when the site is already
	 * configured (organization + business type set) — sites set up before
	 * the wizard existed shouldn't be nagged to re-run it.
	 *
	 * @return array Check result
	 */
	private static function check_setup_complete() {
		$wizard_done = (bool) get_option( 'bw_schema_setup_complete', false );

		$already_configured = false;
		if ( ! $wizard_done ) {
			$org                = BW_Schema_Service_Organization::get();
			$already_configured = ! empty( $org['name'] ) && ! empty( $org['logo'] )
				&& '' !== get_option( 'bw_schema_business_type', '' );
		}

		$passing = $wizard_done || $already_configured;

		return array(
			'id'       => 'setup_complete',
			'passing'  => $passing,
			'title'    => $passing
				? __( 'First-time configuration complete', 'bw-schema' )
				: __( 'Complete the first-time configuration', 'bw-schema' ),
			'detail'   => $passing
				? __( 'The site is configured — re-run the wizard anytime from Tools & Settings', 'bw-schema' )
				: __( 'A four-step guided setup: organization, contact, people, and content — about 5 minutes', 'bw-schema' ),
			'priority' => 'high',
			'effort'   => '5m',
			'link'     => admin_url( 'admin.php?page=bw-schema-setup' ),
			'progress' => '',
		);
	}

	/**
	 * Check: core organization profile fields are filled
	 *
	 * @return array Check result
	 */
	private static function check_organization_profile() {
		$org = BW_Schema_Service_Organization::get();

		$fields = array(
			'name'      => __( 'name', 'bw-schema' ),
			'url'       => __( 'website URL', 'bw-schema' ),
			'logo'      => __( 'logo', 'bw-schema' ),
			'telephone' => __( 'phone number', 'bw-schema' ),
			'email'     => __( 'email', 'bw-schema' ),
		);

		// Fields deliberately marked "not used" are not gaps — drop them
		// from the check entirely (both the missing list and the count)
		$optouts     = BW_Schema_Service_Organization::get_field_optouts();
		$optout_note = array();
		foreach ( $optouts as $optout ) {
			if ( isset( $fields[ $optout ] ) ) {
				$optout_note[] = $fields[ $optout ];
				unset( $fields[ $optout ] );
			}
		}

		$missing = array();
		foreach ( $fields as $key => $label ) {
			if ( empty( $org[ $key ] ) ) {
				$missing[] = $label;
			}
		}

		$filled = count( $fields ) - count( $missing );

		$passing_detail = sprintf(
			/* translators: %s: organization name */
			__( '%s — name, URL, logo, and contact info are set', 'bw-schema' ),
			$org['name']
		);
		if ( ! empty( $optout_note ) ) {
			$passing_detail .= ' ' . sprintf(
				/* translators: %s: comma-separated list of fields marked not used */
				__( '(%s marked not used)', 'bw-schema' ),
				implode( ', ', $optout_note )
			);
		}

		return array(
			'id'       => 'org_profile',
			'passing'  => empty( $missing ),
			'title'    => empty( $missing )
				? __( 'Organization profile complete', 'bw-schema' )
				: __( 'Complete your organization profile', 'bw-schema' ),
			'detail'   => empty( $missing )
				? $passing_detail
				: sprintf(
					/* translators: %s: comma-separated list of missing fields */
					__( 'Missing: %s', 'bw-schema' ),
					implode( ', ', $missing )
				),
			'priority' => 'high',
			'effort'   => '5m',
			'link'     => admin_url( 'admin.php?page=bw-schema-organization' ),
			'progress' => $filled . '/' . count( $fields ),
		);
	}

	/**
	 * Check: business category + type chosen (drives typed schema output)
	 *
	 * @return array Check result
	 */
	private static function check_business_type() {
		$category = get_option( 'bw_schema_business_category', '' );
		$type     = get_option( 'bw_schema_business_type', '' );
		$passing  = ! empty( $category ) && ! empty( $type );

		$detail = __( 'Choosing a business type upgrades your schema from generic Organization to a specific type search engines prefer.', 'bw-schema' );

		if ( $passing && method_exists( 'BW_Schema_Service_Organization', 'get_schema_type' ) ) {
			$schema_type = BW_Schema_Service_Organization::get_schema_type( $category, $type );
			if ( $schema_type ) {
				$detail = sprintf(
					/* translators: %s: schema.org type name */
					__( 'Your site emits schema.org/%s markup', 'bw-schema' ),
					$schema_type
				);
			}
		}

		return array(
			'id'       => 'business_type',
			'passing'  => $passing,
			'title'    => $passing
				? __( 'Business type configured', 'bw-schema' )
				: __( 'Choose your business type', 'bw-schema' ),
			'detail'   => $detail,
			'priority' => 'high',
			'effort'   => '2m',
			'link'     => admin_url( 'admin.php?page=bw-schema-organization' ),
			'progress' => '',
		);
	}

	/**
	 * Check: team members exist (enables Person schema + author linking)
	 *
	 * @return array Check result
	 */
	private static function check_team_members() {
		$count = BW_Schema_Service_People::get_team_member_count();

		return array(
			'id'       => 'team_members',
			'passing'  => $count > 0,
			'title'    => $count > 0
				? sprintf(
					/* translators: %d: number of team members */
					_n( '%d team member with Person schema', '%d team members with Person schema', $count, 'bw-schema' ),
					$count
				)
				: __( 'Add your team members', 'bw-schema' ),
			'detail'   => $count > 0
				? __( 'Each team page emits Person markup linked to your organization', 'bw-schema' )
				: __( 'Team member pages emit Person schema and power author markup on posts', 'bw-schema' ),
			'priority' => 'medium',
			'effort'   => '15m',
			'link'     => admin_url( 'admin.php?page=bw-schema-people' ),
			'progress' => '',
		);
	}

	/**
	 * Check: team members have photos (image is a key Person property)
	 *
	 * Renewable — a newly added member without a photo re-surfaces this check.
	 *
	 * @return array|null Check result, or null when no team exists yet
	 */
	private static function check_team_photos() {
		$members = BW_Schema_Service_People::get_team_members();

		if ( empty( $members ) ) {
			return null; // Covered by the team_members check
		}

		$missing = array();
		foreach ( $members as $member ) {
			if ( ! has_post_thumbnail( $member->ID ) ) {
				$missing[] = get_the_title( $member->ID );
			}
		}

		$total = count( $members );
		$with  = $total - count( $missing );

		return array(
			'id'       => 'team_photos',
			'passing'  => empty( $missing ),
			'title'    => empty( $missing )
				? __( 'All team members have photos', 'bw-schema' )
				: sprintf(
					/* translators: %d: number of team members without a photo */
					_n( '%d team member has no photo', '%d team members have no photo', count( $missing ), 'bw-schema' ),
					count( $missing )
				),
			'detail'   => empty( $missing )
				? __( 'Person schema includes an image for every member', 'bw-schema' )
				: sprintf(
					/* translators: %s: comma-separated member names */
					__( 'Missing photos: %s', 'bw-schema' ),
					implode( ', ', array_slice( $missing, 0, 5 ) ) . ( count( $missing ) > 5 ? '…' : '' )
				),
			'priority' => 'medium',
			'effort'   => '10m',
			'link'     => admin_url( 'edit.php?post_type=' . BW_Schema_Service_People::get_team_post_type() ),
			'progress' => $with . '/' . $total,
		);
	}

	/**
	 * Check: team members have job titles
	 *
	 * Renewable — same as photos.
	 *
	 * @return array|null Check result, or null when no team exists yet
	 */
	private static function check_team_job_titles() {
		$members = BW_Schema_Service_People::get_team_members();

		if ( empty( $members ) ) {
			return null;
		}

		$missing = array();
		foreach ( $members as $member ) {
			$job_title = get_post_meta( $member->ID, 'title', true );
			if ( ! $job_title ) {
				$job_title = get_post_meta( $member->ID, '_job_title', true );
			}
			if ( ! $job_title ) {
				$missing[] = get_the_title( $member->ID );
			}
		}

		$total = count( $members );
		$with  = $total - count( $missing );

		return array(
			'id'       => 'team_job_titles',
			'passing'  => empty( $missing ),
			'title'    => empty( $missing )
				? __( 'All team members have job titles', 'bw-schema' )
				: sprintf(
					/* translators: %d: number of team members without a job title */
					_n( '%d team member has no job title', '%d team members have no job title', count( $missing ), 'bw-schema' ),
					count( $missing )
				),
			'detail'   => empty( $missing )
				? __( 'Person schema includes jobTitle for every member', 'bw-schema' )
				: sprintf(
					/* translators: %s: comma-separated member names */
					__( 'Missing job titles: %s', 'bw-schema' ),
					implode( ', ', array_slice( $missing, 0, 5 ) ) . ( count( $missing ) > 5 ? '…' : '' )
				),
			'priority' => 'medium',
			'effort'   => '10m',
			'link'     => admin_url( 'edit.php?post_type=' . BW_Schema_Service_People::get_team_post_type() ),
			'progress' => $with . '/' . $total,
		);
	}

	/**
	 * Check: a physical location is configured
	 *
	 * Priority scales with business type: location-based categories (food,
	 * accommodation, retail…) get medium priority; others low. Skipped
	 * entirely until a business category is chosen.
	 *
	 * @return array|null Check result, or null when not applicable yet
	 */
	private static function check_locations() {
		$category = get_option( 'bw_schema_business_category', '' );

		if ( empty( $category ) ) {
			return null; // Business type check leads; this follows
		}

		$location_based = array( 'accommodation', 'food_beverage', 'medical', 'retail', 'beauty', 'automotive', 'entertainment' );
		$priority       = in_array( $category, $location_based, true ) ? 'medium' : 'low';

		$locations = BW_Schema_Service_Locations::get_locations();
		$count     = is_array( $locations ) ? count( $locations ) : 0;

		return array(
			'id'       => 'locations',
			'passing'  => $count > 0,
			'title'    => $count > 0
				? sprintf(
					/* translators: %d: number of locations */
					_n( '%d location configured', '%d locations configured', $count, 'bw-schema' ),
					$count
				)
				: __( 'Add your business location', 'bw-schema' ),
			'detail'   => $count > 0
				? __( 'Location data enriches your LocalBusiness schema', 'bw-schema' )
				: __( 'An address enables LocalBusiness schema with map and local-search benefits', 'bw-schema' ),
			'priority' => $priority,
			'effort'   => '5m',
			'link'     => admin_url( 'admin.php?page=bw-schema-organization&tab=locations' ),
			'progress' => '',
		);
	}

	/**
	 * Check: no other plugin is emitting a competing schema graph
	 *
	 * Yoast SEO runs alongside every build; its schema output must be
	 * suppressed so search engines see one Organization entity, not two.
	 * Skipped when Yoast is not active.
	 *
	 * @return array|null Check result, or null when Yoast is not active
	 */
	private static function check_schema_conflicts() {
		if ( ! BW_Schema_Service_Conflicts::is_yoast_active() ) {
			return null;
		}

		$suppressed = BW_Schema_Service_Conflicts::is_yoast_schema_disabled();

		return array(
			'id'       => 'yoast_conflict',
			'passing'  => $suppressed,
			'title'    => $suppressed
				? __( 'Yoast SEO schema output disabled', 'bw-schema' )
				: __( 'Turn off Yoast SEO\'s schema output', 'bw-schema' ),
			'detail'   => $suppressed
				? __( 'Solomon Schema is the single source of structured data — Yoast still handles titles, meta, and sitemaps', 'bw-schema' )
				: __( 'Yoast SEO is emitting its own Organization and breadcrumb schema alongside Solomon Schema — search engines see two conflicting graphs', 'bw-schema' ),
			'priority' => 'high',
			'effort'   => '1m',
			'link'     => admin_url( 'admin.php?page=bw-schema-tools' ),
			'progress' => '',
		);
	}
}
