<?php
namespace Misuzu;

use Index\Environment;
use Index\Data\IDbConnection;
use Index\Data\Migration\IDbMigrationRepo;
use Index\Data\Migration\DbMigrationManager;
use Index\Data\Migration\FsDbMigrationRepo;
use Sasae\SasaeEnvironment;
use Syokuhou\IConfig;
use Misuzu\Template;
use Misuzu\Auth\AuthContext;
use Misuzu\Auth\AuthInfo;
use Misuzu\AuditLog\AuditLog;
use Misuzu\Changelog\Changelog;
use Misuzu\Changelog\ChangelogRoutes;
use Misuzu\Comments\Comments;
use Misuzu\Counters\Counters;
use Misuzu\Emoticons\Emotes;
use Misuzu\Forum\ForumContext;
use Misuzu\Home\HomeRoutes;
use Misuzu\Info\InfoRoutes;
use Misuzu\News\News;
use Misuzu\News\NewsRoutes;
use Misuzu\Perms\Permissions;
use Misuzu\Profile\ProfileFields;
use Misuzu\Satori\SatoriRoutes;
use Misuzu\SharpChat\SharpChatRoutes;
use Misuzu\URLs\URLRegistry;
use Misuzu\Users\UsersContext;
use Misuzu\Users\UserInfo;
use Misuzu\Users\Assets\AssetsRoutes;

// this class should function as the root for everything going forward
// no more magical static classes that are just kind of assumed to exist
// it currently looks Pretty Messy, but most everything else will be holding instances of other classes
// instances of certain classes should only be made as needed,
//  dunno if i want null checks some maybe some kind of init func should be called first like is the case
//  with the http shit
class MisuzuContext {
    private IDbConnection $dbConn;
    private IConfig $config;
    private SasaeEnvironment $templating;

    private AuditLog $auditLog;
    private Counters $counters;

    private Emotes $emotes;
    private Changelog $changelog;
    private News $news;
    private Comments $comments;

    private AuthContext $authCtx;
    private UsersContext $usersCtx;
    private ForumContext $forumCtx;

    private ProfileFields $profileFields;

    private Permissions $perms;
    private AuthInfo $authInfo;
    private SiteInfo $siteInfo;

    // this probably shouldn't be available
    private URLRegistry $urls;

    public function __construct(IDbConnection $dbConn, IConfig $config) {
        $this->dbConn = $dbConn;
        $this->config = $config;

        $this->perms = new Permissions($dbConn);
        $this->authInfo = new AuthInfo($this->perms);
        $this->siteInfo = new SiteInfo($config->scopeTo('site'));

        $this->authCtx = new AuthContext($dbConn, $config->scopeTo('auth'));
        $this->usersCtx = new UsersContext($dbConn);
        $this->forumCtx = new ForumContext($dbConn);

        $this->auditLog = new AuditLog($dbConn);
        $this->changelog = new Changelog($dbConn);
        $this->comments = new Comments($dbConn);
        $this->counters = new Counters($dbConn);
        $this->emotes = new Emotes($dbConn);
        $this->news = new News($dbConn);
        $this->profileFields = new ProfileFields($dbConn);
    }

    public function getDbConn(): IDbConnection {
        return $this->dbConn;
    }

    public function getDbQueryCount(): int {
        $result = $this->dbConn->query('SHOW SESSION STATUS LIKE "Questions"');
        return $result->next() ? $result->getInteger(1) : 0;
    }

    public function createMigrationManager(): DbMigrationManager {
        return new DbMigrationManager($this->dbConn, 'msz_' . DbMigrationManager::DEFAULT_TABLE);
    }

    public function createMigrationRepo(): IDbMigrationRepo {
        return new FsDbMigrationRepo(MSZ_MIGRATIONS);
    }

    public function getURLs(): URLRegistry {
        return $this->urls;
    }

    public function getConfig(): IConfig {
        return $this->config;
    }

    public function getEmotes(): Emotes {
        return $this->emotes;
    }

    public function getChangelog(): Changelog {
        return $this->changelog;
    }

    public function getNews(): News {
        return $this->news;
    }

    public function getComments(): Comments {
        return $this->comments;
    }

    public function getAuditLog(): AuditLog {
        return $this->auditLog;
    }

    public function getCounters(): Counters {
        return $this->counters;
    }

    public function getProfileFields(): ProfileFields {
        return $this->profileFields;
    }

    public function getPerms(): Permissions {
        return $this->perms;
    }

    public function getAuthContext(): AuthContext {
        return $this->authCtx;
    }

    public function getUsersContext(): UsersContext {
        return $this->usersCtx;
    }

    public function getForumContext(): ForumContext {
        return $this->forumCtx;
    }

    public function getAuthInfo(): AuthInfo {
        return $this->authInfo;
    }

    public function getSiteInfo(): SiteInfo {
        return $this->siteInfo;
    }

    public function createAuditLog(string $action, array $params = [], UserInfo|string|null $userInfo = null): void {
        if($userInfo === null && $this->authInfo->isLoggedIn())
            $userInfo = $this->authInfo->getUserInfo();

        $this->auditLog->createLog(
            $userInfo,
            $action,
            $params,
            $_SERVER['REMOTE_ADDR'] ?? '::1',
            $_SERVER['COUNTRY_CODE'] ?? 'XX'
        );
    }

    private ?bool $hasManageAccess = null;
    public function hasManageAccess(): bool {
        $this->hasManageAccess ??= $this->authInfo->isLoggedIn()
            && !$this->usersCtx->hasActiveBan($this->authInfo->getUserInfo())
            && $this->authInfo->getPerms('global')->check(Perm::G_IS_JANITOR);
        return $this->hasManageAccess;
    }

    public function getWebAssetInfo(): ?object {
        return json_decode(file_get_contents(MSZ_ASSETS . '/current.json'));
    }

    private ?string $chatUrl = null;
    public function getChatURL(): string {
        $this->chatUrl ??= $this->config->getString('sockChat.chatPath.normal');
        return $this->chatUrl;
    }

    public function startTemplating(): void {
        $globals = $this->config->getValues([
            ['eeprom.path:s', '', 'eeprom_path'],
            ['eeprom.app:s', '', 'eeprom_app'],
        ]);

        $isDebug = Environment::isDebug();
        $globals['site_info'] = $this->siteInfo;
        $globals['auth_info'] = $this->authInfo;
        $globals['assets'] = $this->getWebAssetInfo();
        $globals['active_ban_info'] = $this->usersCtx->tryGetActiveBan($this->authInfo->getUserInfo());
        $globals['display_timings_info'] = $isDebug || $this->authInfo->getPerms('global')->check(Perm::G_TIMINGS_VIEW);

        $this->templating = new SasaeEnvironment(
            MSZ_TEMPLATES,
            cache: $isDebug ? null : ['Misuzu', GitInfo::hash(true)],
            debug: $isDebug
        );
        $this->templating->addExtension(new MisuzuSasaeExtension($this));
        $this->templating->addGlobal('globals', $globals);

        Template::init($this->templating);
    }

    public function createRouting(): RoutingContext {
        $routingCtx = new RoutingContext();

        $this->urls = $routingCtx->getURLs();
        $routingCtx->registerDefaultErrorPages();

        $routingCtx->register(new HomeRoutes(
            $this->config,
            $this->dbConn,
            $this->siteInfo,
            $this->authInfo,
            $this->changelog,
            $this->comments,
            $this->counters,
            $this->news,
            $this->usersCtx
        ));

        $routingCtx->register(new AssetsRoutes(
            $this->authInfo,
            $this->urls,
            $this->usersCtx
        ));

        $routingCtx->register(new InfoRoutes);

        $routingCtx->register(new NewsRoutes(
            $this->siteInfo,
            $this->authInfo,
            $this->urls,
            $this->news,
            $this->usersCtx,
            $this->comments
        ));

        $routingCtx->register(new ChangelogRoutes(
            $this->siteInfo,
            $this->urls,
            $this->changelog,
            $this->usersCtx,
            $this->authInfo,
            $this->comments
        ));

        $routingCtx->register(new SharpChatRoutes(
            $this->config->scopeTo('sockChat'),
            $this->urls,
            $this->usersCtx,
            $this->authCtx,
            $this->emotes,
            $this->perms,
            $this->authInfo
        ));

        $routingCtx->register(new SatoriRoutes(
            $this->config->scopeTo('satori'),
            $this->usersCtx,
            $this->forumCtx,
            $this->profileFields
        ));

        $routingCtx->register(new LegacyRoutes($this->urls));

        return $routingCtx;
    }
}
