<?php
namespace admin;

class Site
{
  // define APP_SECRET_KEY
  const APP_SECRET_KEY = 'c3cb2537d25c0efc9e573d059d79c3b8';

  static public function finalize_admin_login( array $user, string $domain, string $cookie_name, bool $remember = false ) {

    \S::set_session( 'user', $user );
    \S::delete_session( 'twofa_pending' );

    if ( $remember ) {
      $payloadArr = [
        'login' => $user['login'],
        'ts'    => time()
      ];

      $json = json_encode($payloadArr, JSON_UNESCAPED_SLASHES);
      $sig  = hash_hmac('sha256', $json, self::APP_SECRET_KEY);
      $payload = base64_encode($json . '.' . $sig);

      setcookie( $cookie_name, $payload, [
        'expires'  => time() + (86400 * 14),
        'path'     => '/',
        'domain'   => $domain,
        'secure'   => true,
        'httponly' => true,
        'samesite' => 'Lax',
      ]);
    }
  }

  public static function special_actions()
  {
    global $mdb;

    $sa = \S::get('s-action');
    $domain = preg_replace('/^www\./', '', $_SERVER['SERVER_NAME']);
    $cookie_name = 'admin_remember_' . str_replace( '.', '-', $domain );
    $users = new \Domain\User\UserRepository($mdb);

    switch ($sa)
    {
      case 'user-logon':
      {
        $login = \S::get('login');
        $pass  = \S::get('password');

        $result = $users->logon($login, $pass);

        if ( $result == 1 )
        {
          $user = $users->details($login);

          if ( $user['twofa_enabled'] == 1 )
          {
            \S::set_session( 'twofa_pending', [
              'uid'      => (int)$user['id'],
              'login'    => $login,
              'remember' => (bool)\S::get('remember'),
              'started'  => time(),
            ] );

            if ( !$users->sendTwofaCode( (int)$user['id'] ) )
            {
              \S::alert('Nie udało się wysłać kodu 2FA. Spróbuj ponownie.');
              \S::delete_session('twofa_pending');
              header('Location: /admin/');
              exit;
            }

            header('Location: /admin/user/twofa/');
            exit;
          }
          else
          {
            $user = $users->details($login);

            self::finalize_admin_login(
              $user,
              $domain,
              $cookie_name,
              (bool)\S::get('remember')
            );

            header('Location: /admin/articles/list/');
            exit;
          }
        }
        else
        {
          if ($result == -1)
          {
            \S::alert('Z powodu 5 nieudanych prób Twoje konto zostało zablokowane.');
          }
          else
          {
            \S::alert('Podane hasło jest nieprawidłowe lub użytkownik nie istnieje.');
          }
          header('Location: /admin/');
          exit;
        }
      }
      break;

      case 'user-2fa-verify':
      {
        $pending = \S::get_session('twofa_pending');
        if ( !$pending || empty( $pending['uid'] ) ) {
          \S::alert('Sesja 2FA wygasła. Zaloguj się ponownie.');
          header('Location: /admin/');
          exit;
        }

        $code = trim((string)\S::get('twofa'));
        if (!preg_match('/^\d{6}$/', $code))
        {
          \S::alert('Nieprawidłowy format kodu.');
          header('Location: /admin/user/twofa/');
          exit;
        }

        $ok = $users->verifyTwofaCode((int)$pending['uid'], $code);
        if (!$ok)
        {
          \S::alert('Błędny lub wygasły kod.');
          header('Location: /admin/user/twofa/');
          exit;
        }

        // 2FA OK - finalna sesja
        $user = $users->details($pending['login']);

        self::finalize_admin_login(
          $user,
          $domain,
          $cookie_name,
          $pending['remember'] ? true : false
        );

        header('Location: /admin/articles/list/');
        exit;
      }
      break;

      case 'user-2fa-resend':
      {
        $pending = \S::get_session('twofa_pending');
        if (!$pending || empty($pending['uid']))
        {
          \S::alert('Sesja 2FA wygasła. Zaloguj się ponownie.');
          header('Location: /admin/');
          exit;
        }

        if (!$users->sendTwofaCode((int)$pending['uid'], true))
        {
          \S::alert('Kod można wysłać ponownie po krótkiej przerwie.');
        }
        else
        {
          \S::alert('Nowy kod został wysłany.');
        }
        header('Location: /admin/user/twofa/');
        exit;
      }
      break;

      case 'user-logout':
      {
        setcookie($cookie_name, "", [
          'expires'  => time() - 86400,
          'path'     => '/',
          'domain'   => $domain,
          'secure'   => true,
          'httponly' => true,
          'samesite' => 'Lax',
        ]);
        \S::delete_session('twofa_pending');
        session_destroy();
        header('Location: /admin/');
        exit;
      }
      break;
    }
  }

  /**
   * Mapa nowych kontrolerów: module => fabryka kontrolera (DI)
   * Przy migracji kolejnego kontrolera - dodaj wpis tutaj
   */
  private static $newControllers = [];

  /**
   * Zwraca mapę fabryk kontrolerów (inicjalizacja runtime)
   */
  private static function getControllerFactories(): array
  {
    if ( !empty( self::$newControllers ) )
      return self::$newControllers;

    self::$newControllers = [
      'Articles' => function() {
        global $mdb;

        return new \admin\Controllers\ArticlesController(
          new \Domain\Article\ArticleRepository( $mdb ),
          new \Domain\Languages\LanguagesRepository( $mdb ),
          new \Domain\Layouts\LayoutsRepository( $mdb ),
          new \Domain\Pages\PagesRepository( $mdb )
        );
      },
      'ArticlesArchive' => function() {
        global $mdb;

        return new \admin\Controllers\ArticlesArchiveController(
          new \Domain\Article\ArticleRepository( $mdb )
        );
      },
      'Banners' => function() {
        global $mdb;

        return new \admin\Controllers\BannerController(
          new \Domain\Banner\BannerRepository( $mdb ),
          new \Domain\Languages\LanguagesRepository( $mdb )
        );
      },
      'Settings' => function() {
        global $mdb;

        return new \admin\Controllers\SettingsController(
          new \Domain\Settings\SettingsRepository( $mdb ),
          new \Domain\Languages\LanguagesRepository( $mdb )
        );
      },
      'ProductArchive' => function() {
        global $mdb;

        return new \admin\Controllers\ProductArchiveController(
          new \Domain\Product\ProductRepository( $mdb )
        );
      },
      // Alias dla starego modułu /admin/archive/list/
      'Archive' => function() {
        global $mdb;

        return new \admin\Controllers\ProductArchiveController(
          new \Domain\Product\ProductRepository( $mdb )
        );
      },
      'Dictionaries' => function() {
        global $mdb;

        return new \admin\Controllers\DictionariesController(
          new \Domain\Dictionaries\DictionariesRepository( $mdb ),
          new \Domain\Languages\LanguagesRepository( $mdb )
        );
      },
      'Filemanager' => function() {
        return new \admin\Controllers\FilemanagerController();
      },
      'Users' => function() {
        global $mdb;

        return new \admin\Controllers\UsersController(
          new \Domain\User\UserRepository( $mdb )
        );
      },
      'Languages' => function() {
        global $mdb;

        return new \admin\Controllers\LanguagesController(
          new \Domain\Languages\LanguagesRepository( $mdb )
        );
      },
      'Layouts' => function() {
        global $mdb;

        return new \admin\Controllers\LayoutsController(
          new \Domain\Layouts\LayoutsRepository( $mdb ),
          new \Domain\Languages\LanguagesRepository( $mdb )
        );
      },
      'Newsletter' => function() {
        global $mdb;

        return new \admin\Controllers\NewsletterController(
          new \Domain\Newsletter\NewsletterRepository(
            $mdb,
            new \Domain\Settings\SettingsRepository( $mdb )
          ),
          new \Domain\Newsletter\NewsletterPreviewRenderer()
        );
      },
      'Scontainers' => function() {
        global $mdb;

        return new \admin\Controllers\ScontainersController(
          new \Domain\Scontainers\ScontainersRepository( $mdb ),
          new \Domain\Languages\LanguagesRepository( $mdb )
        );
      },
      'ShopPromotion' => function() {
        global $mdb;

        return new \admin\Controllers\ShopPromotionController(
          new \Domain\Promotion\PromotionRepository( $mdb )
        );
      },
      'ShopCoupon' => function() {
        global $mdb;

        return new \admin\Controllers\ShopCouponController(
          new \Domain\Coupon\CouponRepository( $mdb )
        );
      },
      'ShopAttribute' => function() {
        global $mdb;

        return new \admin\Controllers\ShopAttributeController(
          new \Domain\Attribute\AttributeRepository( $mdb ),
          new \Domain\Languages\LanguagesRepository( $mdb )
        );
      },
      'ShopPaymentMethod' => function() {
        global $mdb;

        return new \admin\Controllers\ShopPaymentMethodController(
          new \Domain\PaymentMethod\PaymentMethodRepository( $mdb )
        );
      },
      'ShopTransport' => function() {
        global $mdb;

        return new \admin\Controllers\ShopTransportController(
          new \Domain\Transport\TransportRepository( $mdb ),
          new \Domain\PaymentMethod\PaymentMethodRepository( $mdb )
        );
      },
      'Pages' => function() {
        global $mdb;

        return new \admin\Controllers\PagesController(
          new \Domain\Pages\PagesRepository( $mdb ),
          new \Domain\Languages\LanguagesRepository( $mdb ),
          new \Domain\Layouts\LayoutsRepository( $mdb )
        );
      },
      'Integrations' => function() {
        global $mdb;

        return new \admin\Controllers\IntegrationsController(
          new \Domain\Integrations\IntegrationsRepository( $mdb )
        );
      },
      'ShopStatuses' => function() {
        global $mdb;

        return new \admin\Controllers\ShopStatusesController(
          new \Domain\ShopStatus\ShopStatusRepository( $mdb )
        );
      },
      'ShopProductSets' => function() {
        global $mdb;

        return new \admin\Controllers\ShopProductSetsController(
          new \Domain\ProductSet\ProductSetRepository( $mdb )
        );
      },
      'ShopProducer' => function() {
        global $mdb;

        return new \admin\Controllers\ShopProducerController(
          new \Domain\Producer\ProducerRepository( $mdb ),
          new \Domain\Languages\LanguagesRepository( $mdb )
        );
      },
      'ShopCategory' => function() {
        global $mdb;

        return new \admin\Controllers\ShopCategoryController(
          new \Domain\Category\CategoryRepository( $mdb ),
          new \Domain\Languages\LanguagesRepository( $mdb )
        );
      },
      'ShopProduct' => function() {
        global $mdb;

        return new \admin\Controllers\ShopProductController(
          new \Domain\Product\ProductRepository( $mdb )
        );
      },
      'ShopClients' => function() {
        global $mdb;

        return new \admin\Controllers\ShopClientsController(
          new \Domain\Client\ClientRepository( $mdb )
        );
      },
    ];

    return self::$newControllers;
  }

  /**
   * Tworzy instancję nowego kontrolera z Dependency Injection
   */
  private static function createController( string $moduleName )
  {
    global $mdb;

    $factories = self::getControllerFactories();
    if ( !isset( $factories[$moduleName] ) )
      return null;

    $factory = $factories[$moduleName];
    if ( !is_callable( $factory ) )
      return null;

    return $factory();
  }

  
  public static function route()
  {
    $_SESSION['admin'] = true;

		if ( \S::get( 'p' ) )
			\S::set_session( 'p' , \S::get( 'p' ) );

		$page = \S::get_session( 'p' );

    // Budowanie nazwy modułu
    $moduleName = '';
    $results = explode( '_', \S::get( 'module' ) );
    if ( is_array( $results ) ) foreach ( $results as $row )
      $moduleName .= ucfirst( $row );

    $action = \S::get( 'action' );

    // 1. Sprawdź czy istnieje nowy kontroler
    $factories = self::getControllerFactories();
    if ( isset( $factories[$moduleName] ) )
    {
      $controller = self::createController( $moduleName );
      if ( $controller )
      {
        if ( method_exists( $controller, $action ) )
        {
          return $controller->$action();
        }

        if ( $moduleName === 'ShopAttribute' )
        {
          \S::alert( 'Nieprawidłowy adres url.' );
          return false;
        }
      }

    }

    // 2. Fallback na stary kontroler
    $class = '\admin\controls\\' . $moduleName;

    if ( class_exists( $class ) and method_exists( new $class, $action ) )
      return call_user_func_array( array( $class, $action ), array() );
    else
    {
      \S::alert( 'Nieprawidłowy adres url.' );
      return false;
    }
  }

  static public function update()
  {
    global $mdb;

    if ( $results = $mdb -> select( 'pp_updates', [ 'name' ], [ 'done' => 0 ] ) )
    {
      foreach ( $results as $row )
      {
        $class = '\admin\factory\Update';
        $method = $row['name'];

        if ( class_exists( $class ) and method_exists( new $class, $method ) )
          call_user_func_array( array( $class, $method ), array() );
      }
    }
  }
}

