<?php
namespace controls;

class Users
{

  public static function permissions( $user_id, $module = '', $action = '' )
  {
    // Pyziak Jacek
    $permissions[ 1 ][ 'projects' ] = true;
    $permissions[ 1 ][ 'finances' ] = true;
    $permissions[ 1 ][ 'wiki' ] = true;
    $permissions[ 1 ][ 'crm' ] = true;
    // Pyziak Grzegorz
    $permissions[ 3 ][ 'projects' ] = true;
    $permissions[ 3 ][ 'finances' ] = true;
    $permissions[ 3 ][ 'wiki' ] = true;
    $permissions[ 3 ][ 'crm' ] = true;
    // Roman Pyrih
    $permissions[ 5 ][ 'projects' ] = true;
    $permissions[ 5 ][ 'finances' ] = false;
    $permissions[ 5 ][ 'wiki' ] = true;
    $permissions[ 5 ][ 'crm' ] = false;

    if ( $action and isset( $permissions[ $user_id ][ $module ][ $action ] ) )
    {
      return $permissions[ $user_id ][ $module ][ $action ];
    }

    if ( isset( $permissions[ $user_id ][ $module ] ) )
    {
      return $permissions[ $user_id ][ $module ];
    }

    return true;
  }

  public static function logout()
  {
    $domain = preg_replace( '#^(http(s)?://)?w{3}\.#', '$1', $_SERVER['SERVER_NAME'] );
    $cookie_name = str_replace( '.', '-', $domain );

    setcookie( $cookie_name, "", strtotime( "-1 year" ), "/", $domain );
    session_destroy();
    header( 'Location: /' );
    exit;
  }

  public static function settings_save()
  {
    global $mdb, $user;

    if ( \factory\Users::settings_save( $user[ 'id' ], \S::get( 'pushover_api' ), \S::get( 'pushover_user' ) ) )
    {
      $user = $mdb -> get( 'users', '*', [ 'id' => $user[ 'id' ] ] );
      \S::set_session( 'user', $user );
      \S::alert( 'Ustawienia zostały zapisane.' );
    }
    header( 'Location: /settings' );
    exit;
  }

  public static function settings()
  {
    global $user;

    if ( !$user )
    {
      header( 'Location: /login' );
      exit;
    }

    return \view\Users::settings(
        $user,
        self::get_cron_dashboard_data()
    );
  }

  public static function settings_cron_status()
  {
    global $user;

    header( 'Content-Type: application/json; charset=utf-8' );
    header( 'Cache-Control: no-store, no-cache, must-revalidate, max-age=0' );
    header( 'Pragma: no-cache' );

    if ( !$user )
    {
      http_response_code( 403 );
      echo json_encode( [ 'status' => 'error', 'message' => 'Brak autoryzacji.' ] );
      exit;
    }

    echo json_encode( [
      'status' => 'ok',
      'data' => self::get_cron_dashboard_data()
    ], JSON_UNESCAPED_UNICODE );
    exit;
  }

  public static function settings_save_google_ads()
  {
    $fields = [
      'google_ads_developer_token',
      'google_ads_client_id',
      'google_ads_client_secret',
      'google_ads_refresh_token',
      'google_merchant_refresh_token',
      'google_ads_manager_account_id',
    ];

    foreach ( $fields as $field )
    {
      \services\GoogleAdsApi::set_setting( $field, \S::get( $field ) );
    }

    \services\GoogleAdsApi::set_setting( 'google_ads_debug_enabled', \S::get( 'google_ads_debug_enabled' ) ? '1' : '0' );

    // wyczyść cached token przy zmianie credentials
    \services\GoogleAdsApi::set_setting( 'google_ads_access_token', null );
    \services\GoogleAdsApi::set_setting( 'google_ads_access_token_expires', null );
    \services\GoogleAdsApi::set_setting( 'google_merchant_access_token', null );
    \services\GoogleAdsApi::set_setting( 'google_merchant_access_token_expires', null );

    \S::alert( 'Ustawienia Google Ads zostały zapisane.' );
    header( 'Location: /settings' );
    exit;
  }

  public static function settings_save_openai()
  {
    \services\GoogleAdsApi::set_setting( 'openai_enabled', \S::get( 'openai_enabled' ) ? '1' : '0' );
    \services\GoogleAdsApi::set_setting( 'openai_api_key', \S::get( 'openai_api_key' ) );
    \services\GoogleAdsApi::set_setting( 'openai_model', \S::get( 'openai_model' ) );

    \S::alert( 'Ustawienia OpenAI zostały zapisane.' );
    header( 'Location: /settings' );
    exit;
  }

  public static function settings_save_claude()
  {
    \services\GoogleAdsApi::set_setting( 'claude_enabled', \S::get( 'claude_enabled' ) ? '1' : '0' );
    \services\GoogleAdsApi::set_setting( 'claude_api_key', \S::get( 'claude_api_key' ) );
    \services\GoogleAdsApi::set_setting( 'claude_model', \S::get( 'claude_model' ) );

    \S::alert( 'Ustawienia Claude zostały zapisane.' );
    header( 'Location: /settings' );
    exit;
  }

  public static function settings_save_gemini()
  {
    \services\GoogleAdsApi::set_setting( 'gemini_enabled', \S::get( 'gemini_enabled' ) ? '1' : '0' );
    \services\GoogleAdsApi::set_setting( 'gemini_api_key', \S::get( 'gemini_api_key' ) );
    \services\GoogleAdsApi::set_setting( 'gemini_model', \S::get( 'gemini_model' ) );

    \S::alert( 'Ustawienia Gemini zostały zapisane.' );
    header( 'Location: /settings' );
    exit;
  }

  public static function settings_save_ai_prompts()
  {
    \services\GoogleAdsApi::set_setting( 'ai_prompt_title_template', trim( (string) \S::get( 'ai_prompt_title_template' ) ) );
    \services\GoogleAdsApi::set_setting( 'ai_prompt_description_template', trim( (string) \S::get( 'ai_prompt_description_template' ) ) );

    \S::alert( 'Prompty AI zostały zapisane.' );
    header( 'Location: /settings' );
    exit;
  }

  private static function get_cron_dashboard_data()
  {
    global $mdb, $settings;

    $base_url = self::get_base_url();
    $clients_total = (int) $mdb -> query(
      "SELECT COUNT(*)
       FROM clients
       WHERE COALESCE( active, 0 ) = 1
         AND TRIM( COALESCE( google_ads_customer_id, '' ) ) <> ''"
    ) -> fetchColumn();
    $merchant_clients_total = (int) $mdb -> query(
      "SELECT COUNT(*)
       FROM clients
       WHERE COALESCE( active, 0 ) = 1
         AND TRIM( COALESCE( google_ads_customer_id, '' ) ) <> ''
         AND TRIM( COALESCE( google_merchant_account_id, '' ) ) <> ''"
    ) -> fetchColumn();
    $facebook_clients_total = (int) $mdb -> query(
      "SELECT COUNT(*)
       FROM clients
       WHERE COALESCE( active, 0 ) = 1
         AND TRIM( COALESCE( facebook_ads_account_id, '' ) ) <> ''"
    ) -> fetchColumn();

    // --- Kampanie ---
    $campaign_stats = $mdb -> query(
      "SELECT COUNT(*) as total,
        SUM(CASE WHEN phase = 'done' THEN 1 ELSE 0 END) as done,
        COUNT(DISTINCT sync_date) as dates_count,
        MIN(CASE WHEN phase != 'done' THEN sync_date END) as active_date
      FROM cron_sync_status WHERE pipeline = 'campaigns'"
    ) -> fetch( \PDO::FETCH_ASSOC );

    $campaign_total = (int) ( $campaign_stats['total'] ?? 0 );
    $campaign_processed = (int) ( $campaign_stats['done'] ?? 0 );
    $campaign_dates_count = max( 1, (int) ( $campaign_stats['dates_count'] ?? 1 ) );
    $campaign_active_date = $campaign_stats['active_date'] ?? '';
    $campaign_remaining = max( 0, $campaign_total - $campaign_processed );
    $campaign_meta = 'Aktywny dzień: ' . ( $campaign_active_date ?: '-' ) . ', okno dni: ' . $campaign_dates_count;
    $campaign_eta_meta = self::build_eta_meta( 'cron_campaigns', $campaign_remaining );
    if ( $campaign_eta_meta !== '' )
    {
      $campaign_meta .= ', ' . $campaign_eta_meta;
    }

    // --- Produkty (3 work units per row: fetch, aggregate_30, aggregate_temp) ---
    $products_stats = $mdb -> query(
      "SELECT COUNT(*) as total,
        SUM(CASE phase
          WHEN 'fetch' THEN 1
          WHEN 'aggregate_30' THEN 2
          WHEN 'done' THEN 3
          ELSE 0
        END) as work_done,
        COUNT(DISTINCT sync_date) as dates_count,
        MIN(CASE WHEN phase != 'done' THEN sync_date END) as active_date
      FROM cron_sync_status WHERE pipeline = 'products'"
    ) -> fetch( \PDO::FETCH_ASSOC );

    $products_row_count = (int) ( $products_stats['total'] ?? 0 );
    $products_work_done = (int) ( $products_stats['work_done'] ?? 0 );
    $products_total = $products_row_count * 3;
    $products_processed = min( $products_total, $products_work_done );
    $products_dates_count = max( 1, (int) ( $products_stats['dates_count'] ?? 1 ) );
    $products_active_date = $products_stats['active_date'] ?? '';
    $products_remaining = max( 0, $products_total - $products_processed );

    $products_phase_label = 'Zakończono';
    if ( $products_active_date )
    {
      $current_phase = $mdb -> query(
        "SELECT phase FROM cron_sync_status cs INNER JOIN clients c ON cs.client_id = c.id AND COALESCE(c.active, 0) = 1 WHERE cs.pipeline = 'products' AND cs.sync_date = :sync_date AND cs.phase != 'done' ORDER BY FIELD(cs.phase, 'pending', 'fetch', 'aggregate_30') ASC LIMIT 1",
        [ ':sync_date' => $products_active_date ]
      ) -> fetchColumn();

      $phase_labels = [
        'pending' => 'Pobieranie',
        'fetch' => 'Agregacja 30 dni',
        'aggregate_30' => 'Agregacja temp'
      ];
      $products_phase_label = $phase_labels[ $current_phase ] ?? 'Zakończono';
    }

    $products_meta = 'Faza: ' . $products_phase_label . ', aktywny dzień: ' . ( $products_active_date ?: '-' ) . ', okno dni: ' . $products_dates_count;
    $products_eta_meta = self::build_eta_meta( 'cron_products', $products_remaining );
    if ( $products_eta_meta !== '' )
    {
      $products_meta .= ', ' . $products_eta_meta;
    }

    // --- Merchant: postep na poziomie produktow (URL z Merchant Center) ---
    $merchant_products_with_offer = (int) $mdb -> query(
      "SELECT COUNT(*)
       FROM products p
       INNER JOIN clients c ON p.client_id = c.id
       WHERE COALESCE( c.active, 0 ) = 1
         AND TRIM( COALESCE( c.google_merchant_account_id, '' ) ) <> ''
         AND TRIM( COALESCE( p.offer_id, '' ) ) <> ''"
    ) -> fetchColumn();

    $merchant_products_with_url = (int) $mdb -> query(
      "SELECT COUNT(*)
       FROM products p
       INNER JOIN clients c ON p.client_id = c.id
       WHERE COALESCE( c.active, 0 ) = 1
         AND TRIM( COALESCE( c.google_merchant_account_id, '' ) ) <> ''
         AND TRIM( COALESCE( p.offer_id, '' ) ) <> ''
         AND TRIM( COALESCE( p.product_url, '' ) ) <> ''
         AND LOWER( TRIM( p.product_url ) ) NOT IN ( '0', '-', 'null' )"
    ) -> fetchColumn();

    $merchant_products_not_found = (int) $mdb -> query(
      "SELECT COUNT(*)
       FROM products p
       INNER JOIN clients c ON p.client_id = c.id
       WHERE COALESCE( c.active, 0 ) = 1
         AND TRIM( COALESCE( c.google_merchant_account_id, '' ) ) <> ''
         AND TRIM( COALESCE( p.offer_id, '' ) ) <> ''
         AND p.product_url IS NULL
         AND COALESCE( p.merchant_url_not_found, 0 ) = 1"
    ) -> fetchColumn();

    $merchant_processed = $merchant_products_with_url + $merchant_products_not_found;
    $merchant_total = max( 1, $merchant_products_with_offer );
    $merchant_remaining_products = max( 0, $merchant_products_with_offer - $merchant_processed );

    $merchant_batch_limit = (int) ( $settings['cron_products_urls_limit_per_client'] ?? 100 );
    if ( $merchant_batch_limit <= 0 )
    {
      $merchant_batch_limit = 100;
    }
    $merchant_remaining_calls = ( $merchant_remaining_products > 0 ) ? (int) ceil( $merchant_remaining_products / $merchant_batch_limit ) : 0;

    $merchant_meta = 'Produkty z URL: ' . $merchant_products_with_url
      . ', brak w MC: ' . $merchant_products_not_found
      . ', pozostało: ' . $merchant_remaining_products
      . ', paczka: ' . $merchant_batch_limit . ' szt.'
      . ', klienci z Merchant ID: ' . $merchant_clients_total;

    $merchant_eta_meta = self::build_eta_meta( 'cron_products_urls', $merchant_remaining_calls );
    if ( $merchant_eta_meta !== '' )
    {
      $merchant_meta .= ', ' . $merchant_eta_meta;
    }

    // --- Facebook Ads --- (postep = klienci zsynchronizowani dzisiaj)
    $facebook_yesterday = date( 'Y-m-d', strtotime( '-1 day' ) );
    $facebook_last_active_date = (string) \services\FacebookAdsApi::get_setting( 'cron_facebook_ads_last_active_date' );
    $facebook_synced_today = ( $facebook_last_active_date === $facebook_yesterday );

    $facebook_processed = $facebook_synced_today ? $facebook_clients_total : 0;
    $facebook_total = $facebook_clients_total;
    $facebook_remaining = $facebook_synced_today ? 0 : 1;

    $facebook_last_success_at = self::format_datetime( \services\FacebookAdsApi::get_setting( 'cron_facebook_ads_last_success_at' ) );
    $facebook_force_client_id = (int) \services\FacebookAdsApi::get_setting( 'cron_facebook_ads_force_client_id' );

    $facebook_meta = 'Zakres: 30 dni do ' . $facebook_yesterday
      . ', klienci z FB ID: ' . $facebook_clients_total
      . ', ostatni sukces: ' . $facebook_last_success_at
      . ', sync dzisiaj: ' . ( $facebook_synced_today ? 'tak' : 'nie' );
    if ( $facebook_force_client_id > 0 )
    {
      $facebook_meta .= ', zakolejkowany klient: #' . $facebook_force_client_id;
    }

    $facebook_eta_meta = self::build_eta_meta( 'cron_facebook_ads', $facebook_remaining );
    if ( $facebook_eta_meta !== '' )
    {
      $facebook_meta .= ', ' . $facebook_eta_meta;
    }

    // --- Supplemental Feed --- (postep = wygenerowany dzisiaj, binarny jak Facebook)
    $feed_today = date( 'Y-m-d' );
    $feed_last_date = (string) \services\GoogleAdsApi::get_setting( 'cron_supplemental_feed_last_date' );
    $feed_done_today = ( $feed_last_date === $feed_today );

    $feed_processed = $feed_done_today ? $merchant_clients_total : 0;
    $feed_total = max( 1, $merchant_clients_total );

    $feed_last_count = (int) \services\GoogleAdsApi::get_setting( 'cron_supplemental_feed_last_count' );
    $feed_files = (int) \services\GoogleAdsApi::get_setting( 'cron_supplemental_feed_files' );

    $feed_meta = 'Klienci z Merchant ID: ' . $merchant_clients_total
      . ', ostatnia generacja: ' . ( $feed_last_date ?: 'nigdy' )
      . ', pliki: ' . $feed_files
      . ', produkty: ' . $feed_last_count
      . ', dzisiaj: ' . ( $feed_done_today ? 'tak' : 'nie' );

    $cron_schedule = [];

    // --- Endpointy CRON ---
    $cron_endpoints = [
      [ 'name' => 'Legacy CRON', 'path' => '/cron.php', 'action' => 'cron_legacy', 'plan' => '' ],
      [ 'name' => 'Cron uniwersalny (Google Ads)', 'path' => '/cron/cron_universal', 'action' => 'cron_universal', 'plan' => 'Co 1 min: kampanie (wczoraj) + frazy/produkty (7 dni wstecz) + Merchant URL + supplemental feed (raz dziennie)' ],
      [ 'name' => 'Cron alertow kampanii (Merchant)', 'path' => '/cron/cron_campaigns_product_alerts_merchant', 'action' => 'cron_campaigns_product_alerts_merchant', 'plan' => 'Co 15 min: alerty produktowe z Google Merchant' ],
      [ 'name' => 'Cron URL produktów (Merchant)', 'path' => '/cron/cron_products_urls', 'action' => 'cron_products_urls', 'plan' => '' ],
      [ 'name' => 'Cron Facebook Ads', 'path' => '/cron/cron_facebook_ads', 'action' => 'cron_facebook_ads', 'plan' => 'Co 5 min: 30 dni wstecz od wczoraj, blokada ponownego pobrania w tym samym dniu' ],
    ];

    $urls = [];
    foreach ( $cron_endpoints as $endpoint )
    {
      $last_key = 'cron_last_invoked_' . $endpoint['action'] . '_at';
      $urls[] = [
        'name' => $endpoint['name'],
        'url' => $base_url . $endpoint['path'],
        'last_invoked_at' => self::format_datetime( \services\GoogleAdsApi::get_setting( $last_key ) ),
        'plan' => (string) ( $endpoint['plan'] ?? '' ),
      ];
    }

    return [
      'overall_last_invoked_at' => self::format_datetime( \services\GoogleAdsApi::get_setting( 'cron_last_invoked_at' ) ),
      'clients_total' => $clients_total,
      'facebook_clients_total' => $facebook_clients_total,
      'progress' => [
        [
          'name' => 'Kampanie',
          'processed' => $campaign_processed,
          'total' => $campaign_total,
          'percent' => self::progress_percent( $campaign_processed, $campaign_total ),
          'meta' => $campaign_meta
        ],
        [
          'name' => 'Produkty',
          'processed' => $products_processed,
          'total' => $products_total,
          'percent' => self::progress_percent( $products_processed, $products_total ),
          'meta' => $products_meta
        ],
        [
          'name' => 'Merchant (URL produktów)',
          'processed' => $merchant_processed,
          'total' => $merchant_total,
          'percent' => self::progress_percent( $merchant_processed, $merchant_total ),
          'meta' => $merchant_meta
        ],
        [
          'name' => 'Facebook Ads',
          'processed' => $facebook_processed,
          'total' => $facebook_total,
          'percent' => self::progress_percent( $facebook_processed, $facebook_total ),
          'meta' => $facebook_meta
        ],
        [
          'name' => 'Supplemental Feed',
          'processed' => $feed_processed,
          'total' => $feed_total,
          'percent' => self::progress_percent( $feed_processed, $feed_total ),
          'meta' => $feed_meta
        ],
      ],
      'schedule' => $cron_schedule,
      'urls' => $urls
    ];
  }

  private static function progress_percent( $processed, $total )
  {
    $processed = (int) $processed;
    $total = (int) $total;

    if ( $total <= 0 )
    {
      return 0;
    }

    return (int) round( min( 100, max( 0, ( $processed / $total ) * 100 ) ) );
  }

  private static function build_eta_meta( $action_name, $remaining_tasks )
  {
    $remaining_tasks = max( 0, (int) $remaining_tasks );

    if ( $remaining_tasks <= 0 )
    {
      return 'Szacowany koniec: zakończono';
    }

    $avg_interval_seconds = (float) \services\GoogleAdsApi::get_setting( 'cron_avg_interval_' . $action_name . '_sec' );
    $last_interval_seconds = (int) \services\GoogleAdsApi::get_setting( 'cron_last_interval_' . $action_name . '_sec' );

    if ( $avg_interval_seconds <= 0 && $last_interval_seconds > 0 )
    {
      $avg_interval_seconds = (float) $last_interval_seconds;
    }

    if ( $avg_interval_seconds <= 0 )
    {
      return 'Szacowany koniec: brak danych o częstotliwości';
    }

    $estimated_seconds = (int) max( 1, round( $remaining_tasks * $avg_interval_seconds ) );
    $eta_timestamp = time() + $estimated_seconds;

    return 'Śr. interwał: '
         . self::format_duration_short( (int) round( $avg_interval_seconds ) )
         . ', szacowany koniec: '
         . date( 'Y-m-d H:i:s', $eta_timestamp )
         . ' (za '
         . self::format_duration_short( $estimated_seconds )
         . ')';
  }

  private static function format_duration_short( $seconds )
  {
    $seconds = max( 0, (int) $seconds );

    if ( $seconds < 60 )
    {
      return $seconds . ' sek';
    }

    $days = (int) floor( $seconds / 86400 );
    $seconds -= $days * 86400;
    $hours = (int) floor( $seconds / 3600 );
    $seconds -= $hours * 3600;
    $minutes = (int) floor( $seconds / 60 );

    $parts = [];
    if ( $days > 0 )
    {
      $parts[] = $days . ' d';
    }
    if ( $hours > 0 )
    {
      $parts[] = $hours . ' h';
    }
    if ( $minutes > 0 )
    {
      $parts[] = $minutes . ' min';
    }

    if ( empty( $parts ) )
    {
      return '1 min';
    }

    return implode( ' ', array_slice( $parts, 0, 2 ) );
  }

  private static function format_datetime( $value )
  {
    $timestamp = strtotime( (string) $value );
    if ( !$timestamp )
    {
      return 'Brak danych';
    }

    return date( 'Y-m-d H:i:s', $timestamp );
  }

  private static function get_base_url()
  {
    $scheme = ( !empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ) ? 'https' : 'http';
    $host = $_SERVER['HTTP_HOST'] ?? ( $_SERVER['SERVER_NAME'] ?? 'localhost' );
    return $scheme . '://' . $host;
  }

  public static function login()
  {
    if ( $user = \factory\Users::login(
        \S::get( 'email' ),
        md5( \S::get( 'password' ) )
      ) )
    {
      // zapamiętaj logowanie
      if ( \S::get( 'remember' ) )
      {
        $domain = preg_replace( '#^(http(s)?://)?w{3}\.#', '$1', $_SERVER['SERVER_NAME'] );
        $cookie_name = str_replace( '.', '-', $domain );

        $value = [ 'email' => \S::get( 'email' ), 'hash' => md5( \S::get( 'password' ) ) ];
        $value = json_encode( $value );

        setcookie( $cookie_name, $value, strtotime( "+1 year" ), "/", $domain );
      }

      \S::set_session( 'user', $user );
      echo json_encode( [ 'result' => 'true', 'msg' => 'Właśnie zostałeś zalogowany. Za chwilę nastąpi przekierowanie.', 'default_project' => $user[ 'default_project' ] ] );
    }
    else
    {
      echo json_encode( [ 'result' => 'false', 'msg' => 'Podany login i hasło są nieprawidłowe.' ] );
    }
    exit;
  }

  public static function login_form()
  {
    return \Tpl::view( 'users/login-form' );
  }

}

