<?php

namespace Blazemedia\Blackhole;

use Blazemedia\Blackhole\Tools\SimpleDB;
use Blazemedia\Blackhole\Types\PostItem;

class OfferListProvider {

    const DEFAULT_CUTOFF_DAYS = 180;
    const DEFAULT_TRAFFIC_LOOKBACK_DAYS = 7;
    const DEFAULT_USERS_THRESHOLD = 10;
    const DEFAULT_ANALYTICS_ENABLED = true;

    private array $dryRunCandidates = array();
    private int $trafficLookbackDays = self::DEFAULT_TRAFFIC_LOOKBACK_DAYS;
    private int $usersThreshold = self::DEFAULT_USERS_THRESHOLD;
    private bool $analyticsEnabled = self::DEFAULT_ANALYTICS_ENABLED;
    private array $settings = array();
    private int $scannedPostsCount = 0;
    private int $queuedPostsCount = 0;


    function __construct(
        protected SimpleDB $db,
        private bool $dryRun = false
        ) {
        $this->settings = get_option( 'blackhole_settings_option', array() );
        $this->trafficLookbackDays = $this->resolveTrafficLookbackDays();
        $this->usersThreshold = $this->resolveUsersThreshold();
        $this->analyticsEnabled = $this->resolveAnalyticsEnabled();
    }


    function scan() {

        $beforeTimestamp = $this->getCutoffTimestamp();
        $cutoffDayStart = strtotime( date( 'Y-m-d 00:00:00', $beforeTimestamp ) );

        $this->logLine( "Before: " . date('Y-m-d', $beforeTimestamp ) );

        if ( $this->dryRun ) {
            $yesterdayStart = strtotime( 'yesterday 00:00:00' );
            $todayStart = strtotime( 'today 00:00:00' );

            $this->logLine( "Dry run window: " . date( 'Y-m-d', $yesterdayStart ) );
            $this->logLine( "Progress: 1/1 (100%)" );
            $dayRows = $this->db->forEachOfferPostInRange( [$this, 'checkWpPost'], $yesterdayStart, $todayStart );
            $this->logLine( "Rows fetched for day: {$dayRows}" );
            $this->printScanSummary();
            return;
        }

        $this->logLine( "Calculating resume point..." );
        $resumeQueryStartedAt = microtime( true );
        $oldestTimestamp = $this->db->getOldestUnprocessedOfferPostTimestampBefore( $beforeTimestamp );
        $resumeQueryElapsed = round( microtime( true ) - $resumeQueryStartedAt, 2 );
        $this->logLine( "Resume point lookup completed in {$resumeQueryElapsed}s" );

        if ( $oldestTimestamp <= 0 ) {
            $this->logLine( "No unprocessed posts found in selected range." );
            return;
        }

        $this->logLine( "Resume from: " . date( 'Y-m-d', $oldestTimestamp ) );
        $cursor = strtotime( date( 'Y-m-d 00:00:00', $oldestTimestamp ) );
        $totalDays = max( 1, (int) ceil( ( $cutoffDayStart - $cursor ) / DAY_IN_SECONDS ) );
        $dayIndex = 0;

        while ( $cursor < $cutoffDayStart ) {
            $dayIndex++;
            $nextCursor = $cursor + DAY_IN_SECONDS;
            $progressPercentage = (int) floor( ( $dayIndex / $totalDays ) * 100 );
            $this->logLine( sprintf(
                "Processing day %d/%d (%d%%): %s\n",
                $dayIndex,
                $totalDays,
                $progressPercentage,
                date( 'Y-m-d', $cursor )
            ) );
            $dayStartedAt = microtime( true );
            $dayRows = $this->db->forEachOfferPostInRange( [$this, 'checkWpPost'], $cursor, $nextCursor );
            $dayElapsed = round( microtime( true ) - $dayStartedAt, 2 );
            $this->logLine( sprintf(
                "Day query result -> rows: %d | elapsed: %.2fs",
                $dayRows,
                $dayElapsed
            ) );
            $this->logLine( sprintf(
                "Current totals -> analyzed: %d | queued: %d\n",
                $this->scannedPostsCount,
                $this->queuedPostsCount
            ) );
            $cursor = $nextCursor;
        }

        $this->printScanSummary();
    }


    public function checkWpPost( $wp_post ){
        /// if already inserted do nothing
        if( !$this->dryRun && $this->db->exists( $wp_post->ID) ) return;
        
        $post = new PostItem( $wp_post, $this->trafficLookbackDays, $this->analyticsEnabled );
        $this->scannedPostsCount++;

        $this->logLine( json_encode( $post->toArray() ) );

        if( $this->hasNotEnoughTraffic( $post ) ) $this->insertIntoList( $post );
    }


    function insertIntoList( PostItem $post ) {
        $this->queuedPostsCount++;

        if ( $this->dryRun ) {
            $this->dryRunCandidates[] = $post->toArray();
            return;
        }

        $this->db->save( $post );
    }


    function hasNotEnoughTraffic( $post ) {
        if ( !$this->analyticsEnabled ) {
            return true;
        }

        return $post->users < $this->usersThreshold;
    }

    public function getDryRunCandidates() : array {
        return $this->dryRunCandidates;
    }

    public function getScannedPostsCount() : int {
        return $this->scannedPostsCount;
    }

    public function getQueuedPostsCount() : int {
        return $this->queuedPostsCount;
    }

    private function getCutoffTimestamp() : int {
        $mode = isset( $this->settings['cutoff_mode'] ) ? sanitize_key( $this->settings['cutoff_mode'] ) : 'days';

        if ( $mode === 'fixed_date' && !empty( $this->settings['cutoff_date'] ) ) {
            $fixedDate = sanitize_text_field( $this->settings['cutoff_date'] );
            $fixedTimestamp = strtotime( $fixedDate . ' 23:59:59' );

            if ( $fixedTimestamp !== false ) {
                return $fixedTimestamp;
            }
        }

        $days = isset( $this->settings['cutoff_days'] ) ? absint( $this->settings['cutoff_days'] ) : self::DEFAULT_CUTOFF_DAYS;
        if ( $days <= 0 ) {
            $days = self::DEFAULT_CUTOFF_DAYS;
        }

        return strtotime('now') - ( DAY_IN_SECONDS * $days );
    }

    private function resolveTrafficLookbackDays() : int {
        $days = isset( $this->settings['traffic_lookback_days'] ) ? absint( $this->settings['traffic_lookback_days'] ) : self::DEFAULT_TRAFFIC_LOOKBACK_DAYS;
        return $days > 0 ? $days : self::DEFAULT_TRAFFIC_LOOKBACK_DAYS;
    }

    private function resolveUsersThreshold() : int {
        $threshold = isset( $this->settings['users_threshold'] ) ? absint( $this->settings['users_threshold'] ) : self::DEFAULT_USERS_THRESHOLD;
        return $threshold >= 0 ? $threshold : self::DEFAULT_USERS_THRESHOLD;
    }

    private function resolveAnalyticsEnabled() : bool {
        if ( !array_key_exists( 'analytics_enabled', $this->settings ) ) {
            return self::DEFAULT_ANALYTICS_ENABLED;
        }

        return !empty( $this->settings['analytics_enabled'] );
    }

    private function printScanSummary() : void {
        $this->logLine( sprintf(
            "Scan summary -> analyzed: %d | queued: %d\n",
            $this->scannedPostsCount,
            $this->queuedPostsCount
        ) );
    }

    private function logLine( string $message ) : void {
        $message = rtrim( $message, "\n" );

        if ( defined( 'WP_CLI' ) && WP_CLI ) {
            \WP_CLI::log( $message );
            return;
        }

        echo $message . "\n";
        if ( function_exists( 'ob_flush' ) ) {
            @ob_flush();
        }
        if ( function_exists( 'flush' ) ) {
            flush();
        }
    }

}
