<?php

namespace Controllers;

use Libs\ResponseLib;
use Psr\Http\Message\ServerRequestInterface;
use Models\CprModel;
use Models\DiscountModel;
use Models\StatusModel;
use Services\PaymentService;

class B3CprRegisterController
{
    private CprModel $cprModel;
    private DiscountModel $discountModel;
    private StatusModel $statusModel;
    private PaymentService $paymentService;

    private const PAYMENT_VALUE = 1000000;

    public function __construct()
    {
        $this->cprModel = new CprModel();
        $this->discountModel = new DiscountModel();
        $this->statusModel = new StatusModel();
        $this->paymentService = new PaymentService();
    }

    private function applyFixedCprDefaults(array $cpr): array
    {
        $nowBr = new \DateTimeImmutable('now', new \DateTimeZone('America/Sao_Paulo'));
        $currentDate = $nowBr->format('Y-m-d');

        $cpr['cpr_type_code'] = 'P';
        $cpr['cpr_otc_register_account_code'] = '64359.40-5';
        $cpr['cpr_otc_payment_agent_account_code'] = '64359.40-5';
        $cpr['cpr_otc_custodian_account_code'] = '64359.00-3';
        $cpr['cpr_electronic_emission_indicator'] = 'S';
        $cpr['cpr_automatic_expiration_indicator'] = 'N';
        $cpr['cpr_issue_date'] = $currentDate;
        $cpr['cpr_profitability_start_date'] = $currentDate;
        $cpr['cpr_issue_quantity'] = '1';

        if (!array_key_exists('cpr_issue_value', $cpr)) {
            throw new \InvalidArgumentException('Missing field: cpr_issue_value');
        }
        $issueValue = (string)$cpr['cpr_issue_value'];
        $cpr['cpr_issue_financial_value'] = $issueValue;

        $cpr['cpr_creditor_name'] = 'TOO EASY TRADING LTDA';
        $cpr['cpr_creditor_document_number'] = '47.175.222/0001-09';
        $cpr['cpr_scr_type_code'] = 'N';
        $cpr['cpr_finality_code'] = '6099';

        return $cpr;
    }

    private function calculatePaymentValue(?string $discountCode): int
    {
        $baseValue = self::PAYMENT_VALUE;
        if ($discountCode === null) {
            return $baseValue;
        }

        $normalized = trim($discountCode);
        if ($normalized === '') {
            return $baseValue;
        }

        try {
            $row = $this->discountModel->findByCode($normalized);
        } catch (\Throwable $e) {
            return $baseValue;
        }

        if (!$row) {
            return $baseValue;
        }

        $value = (int)($row['discount_value'] ?? 0);
        if ($value <= 0) {
            return $baseValue;
        }

        return $value;
    }

    public function __invoke(ServerRequestInterface $request)
    {
        $timezone = $_ENV['APP_TIMEZONE'] ?? 'America/Sao_Paulo';
        $now = new \DateTimeImmutable('now', new \DateTimeZone($timezone));
        $hour = (int)$now->format('H');
        if ($hour >= 20 || $hour < 8) {
            return ResponseLib::sendFail(
                'B3 se encontra offline no momento. Tente novamente entre 08:00 e 20:00.',
                ['current_time' => $now->format('Y-m-d H:i:s'), 'timezone' => $timezone],
                'E_B3_OFFLINE'
            )->withStatus(503);
        }

        $body = json_decode((string)$request->getBody(), true);
        if (!is_array($body)) {
            return ResponseLib::sendFail('Invalid JSON body', [], 'E_VALIDATE')->withStatus(400);
        }

        $cpr = $body['cpr'] ?? null;

        if (!is_array($cpr)) {
            $hasCprKeys = false;
            foreach ($body as $k => $_) {
                if (is_string($k) && substr($k, 0, 4) === 'cpr_') {
                    $hasCprKeys = true;
                    break;
                }
            }
            if ($hasCprKeys) {
                $cpr = $body;
            }
        }

        if (!is_array($cpr)) {
            return ResponseLib::sendFail('Missing CPR payload (array) in body as cpr', [], 'E_VALIDATE')->withStatus(400);
        }

        try {
            $cpr = $this->applyFixedCprDefaults($cpr);
        } catch (\InvalidArgumentException $e) {
            return ResponseLib::sendFail($e->getMessage(), [], 'E_VALIDATE')->withStatus(400);
        }

        if (!array_key_exists('cpr_ticker', $cpr)) {
            $cpr['cpr_ticker'] = '';
        }

        $userId = (int)($request->getAttribute('api_user_id') ?? 0);
        if ($userId <= 0) {
            return ResponseLib::sendFail('Authenticated user not found', [], 'E_VALIDATE')->withStatus(401);
        }

        $companyId = (int)($request->getAttribute('api_company_id') ?? 0);
        if ($companyId <= 0) {
            return ResponseLib::sendFail('Authenticated company not found', [], 'E_VALIDATE')->withStatus(401);
        }

        $statusId = $this->statusModel->getIdByStatus('pending');
        if ($statusId === null) {
            return ResponseLib::sendFail('Pending status not found', [], 'E_DATABASE')->withStatus(500);
        }

        $discountCode = isset($body['discount']) ? (string)$body['discount'] : null;
        $paymentValue = $this->calculatePaymentValue($discountCode);

        try {
            $paymentData = $this->paymentService->initiatePayment($paymentValue);
        } catch (\Throwable $e) {
            return ResponseLib::sendFail('Failed to initiate payment: ' . $e->getMessage(), [], 'E_INTERNAL')->withStatus(500);
        }

        try {
            $record = $this->cprModel->create($cpr, $statusId, (int)$paymentData['payment_id'], $userId, $companyId);
        } catch (\InvalidArgumentException $e) {
            return ResponseLib::sendFail($e->getMessage(), [], 'E_VALIDATE')->withStatus(400);
        } catch (\Throwable $e) {
            return ResponseLib::sendFail('Failed to create CPR: ' . $e->getMessage(), [], 'E_DATABASE')->withStatus(500);
        }

        return ResponseLib::sendOk([
            'cpr_id' => $record['cpr_id'] ?? null,
            'payment_id' => $paymentData['payment_id'],
            'payment_code' => $paymentData['payment_code'],
        ], 'S_CREATED');
    }
}