<?php
namespace admin;

class App
{
  const APP_SECRET_KEY = 'c3cb2537d25c0efc9e573d059d79c3b8';

  /**
   * Mapa nowych kontrolerów: module => fabryka kontrolera (DI)
   */
  private static $newControllers = [];

  public static function finalize_admin_login( array $user, string $domain, string $cookie_name, bool $remember = false )
  {
    \Shared\Helpers\Helpers::set_session( 'user', $user );
    \Shared\Helpers\Helpers::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 = \Shared\Helpers\Helpers::get( 's-action' );
    if ( !$sa ) return;

    $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 = \Shared\Helpers\Helpers::get( 'login' );
        $pass  = \Shared\Helpers\Helpers::get( 'password' );
        $result = $users->logon( $login, $pass );

        if ( $result == 1 )
        {
          $user = $users->details( $login );
          if ( !$user ) {
            \Shared\Helpers\Helpers::alert( 'Błąd logowania.' );
            header( 'Location: /admin/' );
            exit;
          }

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

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

            header( 'Location: /admin/user/twofa/' );
            exit;
          }

          self::finalize_admin_login( $user, $domain, $cookie_name, (bool) \Shared\Helpers\Helpers::get( 'remember' ) );
          header( 'Location: /admin/articles/list/' );
          exit;
        }

        if ( $result == -1 )
          \Shared\Helpers\Helpers::alert( 'Z powodu 5 nieudanych prób Twoje konto zostało zablokowane.' );
        else
          \Shared\Helpers\Helpers::alert( 'Podane hasło jest nieprawidłowe lub użytkownik nie istnieje.' );

        header( 'Location: /admin/' );
        exit;

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

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

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

        $user = $users->details( $pending['login'] );
        if ( !$user ) {
          \Shared\Helpers\Helpers::delete_session( 'twofa_pending' );
          \Shared\Helpers\Helpers::alert( 'Sesja wygasła. Zaloguj się ponownie.' );
          header( 'Location: /admin/' );
          exit;
        }
        self::finalize_admin_login( $user, $domain, $cookie_name, !empty( $pending['remember'] ) );
        header( 'Location: /admin/articles/list/' );
        exit;

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

        if ( !$users->sendTwofaCode( (int) $pending['uid'], true ) )
          \Shared\Helpers\Helpers::alert( 'Kod można wysłać ponownie po krótkiej przerwie.' );
        else
          \Shared\Helpers\Helpers::alert( 'Nowy kod został wysłany.' );

        header( 'Location: /admin/user/twofa/' );
        exit;

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

  /**
   * Entry point — auth check + layout rendering.
   */
  public static function render(): string
  {
    global $user;

    if ( \Shared\Helpers\Helpers::get( 'module' ) === 'user' && \Shared\Helpers\Helpers::get( 'action' ) === 'twofa' ) {
      $controller = self::createController( 'Users' );
      return $controller->twofa();
    }

    if ( !$user || !$user['admin'] )
    {
      $controller = self::createController( 'Users' );
      return $controller->login_form();
    }

    $tpl = new \Shared\Tpl\Tpl;
    $tpl->content = self::route();
    return $tpl->render( 'site/main-layout' );
  }

  /**
   * Routing — buduje nazwę modułu z URL i wywołuje akcję kontrolera.
   */
  public static function route()
  {
    $_SESSION['admin'] = true;

    if ( \Shared\Helpers\Helpers::get( 'p' ) )
      \Shared\Helpers\Helpers::set_session( 'p', \Shared\Helpers\Helpers::get( 'p' ) );

    // Budowanie nazwy modułu: shop_product → ShopProduct
    $moduleName = '';
    $parts = explode( '_', (string) \Shared\Helpers\Helpers::get( 'module' ) );
    foreach ( $parts as $part )
      $moduleName .= ucfirst( $part );

    $action = \Shared\Helpers\Helpers::get( 'action' );

    $controller = self::createController( $moduleName );
    if ( $controller && method_exists( $controller, $action ) )
      return $controller->$action();

    \Shared\Helpers\Helpers::alert( 'Nieprawidłowy adres url.' );
    return false;
  }

  /**
   * Tworzy instancję kontrolera z Dependency Injection.
   */
  private static function createController( string $moduleName )
  {
    $factories = self::getControllerFactories();
    if ( !isset( $factories[$moduleName] ) )
      return null;

    $factory = $factories[$moduleName];
    return is_callable( $factory ) ? $factory() : null;
  }

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

    self::$newControllers = [
      'Dashboard' => function() {
        global $mdb;
        return new \admin\Controllers\DashboardController(
          new \Domain\Dashboard\DashboardRepository( $mdb ),
          new \Domain\ShopStatus\ShopStatusRepository( $mdb )
        );
      },
      '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 )
        );
      },
      '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 ),
          new \Domain\Integrations\IntegrationsRepository( $mdb ),
          new \Domain\Languages\LanguagesRepository( $mdb )
        );
      },
      'ShopClients' => function() {
        global $mdb;
        return new \admin\Controllers\ShopClientsController(
          new \Domain\Client\ClientRepository( $mdb )
        );
      },
      'ShopOrder' => function() {
        global $mdb;
        $productRepo = new \Domain\Product\ProductRepository( $mdb );
        return new \admin\Controllers\ShopOrderController(
          new \Domain\Order\OrderAdminService(
            new \Domain\Order\OrderRepository( $mdb ),
            $productRepo,
            new \Domain\Settings\SettingsRepository( $mdb ),
            new \Domain\Transport\TransportRepository( $mdb ),
            new \Domain\CronJob\CronJobRepository( $mdb )
          ),
          $productRepo
        );
      },
      'Update' => function() {
        global $mdb;
        return new \admin\Controllers\UpdateController(
          new \Domain\Update\UpdateRepository( $mdb )
        );
      },
    ];

    return self::$newControllers;
  }

  public static function update()
  {
    global $mdb;

    $repository = new \Domain\Update\UpdateRepository( $mdb );
    $repository->runPendingMigrations();
  }
}
