<?php

declare(strict_types=1);

namespace FecaMailshots;

use FecaMailshots\Admin\AttachmentsAdminPage;
use FecaMailshots\Admin\DataSourcesAdminPage;
use FecaMailshots\Admin\MailshotsAdminPage;
use FecaMailshots\Admin\MailshotTestAdminPage;
use FecaMailshots\Admin\PdfAssetsAdminPage;
use FecaMailshots\Admin\ProfileAdminPage;
use FecaMailshots\Admin\ReviewRecipientsAdminPage;
use FecaMailshots\Admin\RunMailshotAdminPage;
use FecaMailshots\Admin\DownloadPdfAdminPage;
use FecaMailshots\Admin\SetupAdminPage;
use FecaMailshots\Application\AttachmentService;
use FecaMailshots\Application\DataSourceService;
use FecaMailshots\Application\DslCompiler;
use FecaMailshots\Application\DslValidator;
use FecaMailshots\Application\MailCredentialsProvider;
use FecaMailshots\Application\MailshotRunService;
use FecaMailshots\Application\MailshotService;
use FecaMailshots\Application\PerUserMailCredentialsProvider;
use FecaMailshots\Application\PdfAssetService;
use FecaMailshots\Application\SecretKeyProvider;
use FecaMailshots\Application\TemplateRenderer;
use FecaMailshots\Domain\DslParser;
use FecaMailshots\Infrastructure\BasicSmtpSender;
use FecaMailshots\Infrastructure\DatabaseRouter;
use FecaMailshots\Infrastructure\MailshotSchemaInstaller;
use FecaMailshots\Infrastructure\DatabaseSourceMetadataProvider;
use FecaMailshots\Infrastructure\PhpImapAppender;
use FecaMailshots\Infrastructure\PdoDatabaseRouter;
use FecaMailshots\Infrastructure\SchemaEnsuringDatabaseRouter;
use FecaMailshots\Infrastructure\WordPressSaltSecretKeyProvider;
use FecaMailshots\Repository\AttachmentRepository;
use FecaMailshots\Repository\LastRunRepository;
use FecaMailshots\Repository\MailCredentialRepository;
use FecaMailshots\Repository\MailshotQueryRepository;
use FecaMailshots\Repository\MailshotRepository;
use FecaMailshots\Repository\PdfAssetRepository;
use FecaMailshots\Support\Container;
use FecaMailshots\Support\ErrorLogLogger;
use FecaMailshots\WordPress\WordPressFacade;
use FecaMailshots\Application\SmtpSender;
use FecaMailshots\Application\ImapAppender;

final class Plugin
{
    public static function buildContainer(array $dbConfig, WordPressFacade $wp, ?DatabaseRouter $router = null): Container
    {
        $c = new Container();

        $c->set('logger', static fn() => new ErrorLogLogger());

        $c->set(DatabaseRouter::class, static fn() => $router ?? new SchemaEnsuringDatabaseRouter(new PdoDatabaseRouter($dbConfig)));
        $c->set(MailshotSchemaInstaller::class, static fn(Container $c) => new MailshotSchemaInstaller($c->get(DatabaseRouter::class)));

        $c->set(DatabaseSourceMetadataProvider::class, static fn(Container $c) => new DatabaseSourceMetadataProvider($c->get(DatabaseRouter::class)));

        $c->set(DslParser::class, static fn() => new DslParser());
        $c->set(DslValidator::class, static fn(Container $c) => new DslValidator($c->get(DatabaseSourceMetadataProvider::class)));
        $c->set(DslCompiler::class, static fn(Container $c) => new DslCompiler($c->get(DatabaseSourceMetadataProvider::class)));

        $c->set(MailshotQueryRepository::class, static fn(Container $c) => new MailshotQueryRepository($c->get(DatabaseRouter::class)));
        $c->set(MailshotRepository::class, static fn(Container $c) => new MailshotRepository($c->get(DatabaseRouter::class)));
        $c->set(AttachmentRepository::class, static fn(Container $c) => new AttachmentRepository($c->get(DatabaseRouter::class)));
        $c->set(PdfAssetRepository::class, static fn(Container $c) => new PdfAssetRepository($c->get(DatabaseRouter::class)));
        $c->set(LastRunRepository::class, static fn(Container $c) => new LastRunRepository($c->get(DatabaseRouter::class)));
        $c->set(SecretKeyProvider::class, static fn() => new WordPressSaltSecretKeyProvider());
        $c->set(MailCredentialRepository::class, static fn(Container $c) => new MailCredentialRepository(
            $c->get(DatabaseRouter::class),
            $c->get(SecretKeyProvider::class)
        ));

        $c->set(DataSourceService::class, static fn(Container $c) => new DataSourceService(
            $c->get(MailshotQueryRepository::class),
            $c->get(DatabaseRouter::class),
            $c->get(DslParser::class),
            $c->get(DslValidator::class),
            $c->get(DslCompiler::class),
            $c->get(DatabaseSourceMetadataProvider::class),
            $c->get(MailshotRepository::class)
        ));

        $c->set(MailshotService::class, static fn(Container $c) => new MailshotService(
            $c->get(MailshotRepository::class),
            $c->get(MailshotQueryRepository::class),
            $c->get(AttachmentRepository::class),
            $c->get(LastRunRepository::class),
            $c->get(DataSourceService::class)
        ));

        $c->set(AttachmentService::class, static fn(Container $c) => new AttachmentService($c->get(AttachmentRepository::class)));
        $c->set(PdfAssetService::class, static fn(Container $c) => new PdfAssetService($c->get(PdfAssetRepository::class)));
        $c->set(TemplateRenderer::class, static fn(Container $c) => new TemplateRenderer(
            static function (string $name) use ($c): ?array {
                return $c->get(PdfAssetRepository::class)->findByName($name);
            }
        ));
        $c->set(SmtpSender::class, static fn() => new BasicSmtpSender());
        $c->set(ImapAppender::class, static fn() => new PhpImapAppender());
        $c->set(MailCredentialsProvider::class, static fn(Container $c) => new PerUserMailCredentialsProvider(
            $c->get(MailCredentialRepository::class),
            $wp
        ));
        $c->set(MailshotRunService::class, static fn(Container $c) => new MailshotRunService(
            $c->get(MailshotRepository::class),
            $c->get(MailshotQueryRepository::class),
            $c->get(AttachmentRepository::class),
            $c->get(DataSourceService::class),
            $c->get(TemplateRenderer::class),
            $c->get(SmtpSender::class),
            $c->get(ImapAppender::class),
            $c->get(MailCredentialsProvider::class),
            $c->get(LastRunRepository::class)
        ));

        $c->set(DataSourcesAdminPage::class, static fn(Container $c) => new DataSourcesAdminPage(
            static fn(): DataSourceService => $c->get(DataSourceService::class),
            $wp
        ));

        $c->set(MailshotsAdminPage::class, static fn(Container $c) => new MailshotsAdminPage(
            static fn(): MailshotService => $c->get(MailshotService::class),
            $wp
        ));

        $c->set(AttachmentsAdminPage::class, static fn(Container $c) => new AttachmentsAdminPage(
            static fn(): AttachmentService => $c->get(AttachmentService::class),
            $wp
        ));

        $c->set(PdfAssetsAdminPage::class, static fn(Container $c) => new PdfAssetsAdminPage(
            static fn(): PdfAssetService => $c->get(PdfAssetService::class),
            $wp
        ));

        $c->set(SetupAdminPage::class, static fn() => new SetupAdminPage($wp));
        $c->set(ProfileAdminPage::class, static fn(Container $c) => new ProfileAdminPage(
            $wp,
            static fn(): MailCredentialRepository => $c->get(MailCredentialRepository::class)
        ));
        $c->set(MailshotTestAdminPage::class, static fn(Container $c) => new MailshotTestAdminPage(
            static fn(): MailshotRunService => $c->get(MailshotRunService::class),
            static fn(): MailshotService => $c->get(MailshotService::class),
            $wp
        ));
        $c->set(RunMailshotAdminPage::class, static fn(Container $c) => new RunMailshotAdminPage(
            static fn(): MailshotRunService => $c->get(MailshotRunService::class),
            static fn(): MailshotService => $c->get(MailshotService::class),
            $wp
        ));
        $c->set(ReviewRecipientsAdminPage::class, static fn(Container $c) => new ReviewRecipientsAdminPage(
            static fn(): DataSourceService => $c->get(DataSourceService::class),
            $wp
        ));
        $c->set(DownloadPdfAdminPage::class, static fn(Container $c) => new DownloadPdfAdminPage(
            static fn(): MailshotRunService => $c->get(MailshotRunService::class),
            static fn(): MailshotService => $c->get(MailshotService::class),
            $wp
        ));

        return $c;
    }
}
