<?php

declare(strict_types=1);

namespace backend\controllers;

use Yii;
use backend\models\Accounts;
use backend\models\Accountssearch;
use backend\models\System;
use backend\services\AccountService;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\web\Response;
use yii\filters\AccessControl;
use yii\filters\VerbFilter;
use yii\data\ActiveDataProvider;

/**
 * AccountsController handles CRUD operations for accounts.
 * 
 * This controller manages account operations including creating, reading,
 * updating, and deleting accounts, as well as providing API endpoints
 * for account lookups.
 * 
 * @author Andrew Pickstock
 * @since 1.0
 */
class AccountsController extends Controller
{
    const FIND_ACCOUNT = 'findAccount';
    const FIND_ACCOUNT_EXTENDED = 'findAccountExtended';
    /**
     * @var bool Whether this controller is enabled for the current project
     */
    private bool $used = true;

    /**
     * @var string Fully qualified class name for the Accounts model
     */
    private string $accountsClass;
    
    /**
     * @var string Fully qualified class name for the Accounts_extended model
     */
    private string $accountExtendedClass;
    
    /**
     * @var string View location alias
     */
    private string $viewLocation;
    
    /**
     * @var AccountService|null Account service instance
     */
    private ?AccountService $accountService = null;

    /**
     * {@inheritdoc}
     */
    public function __construct(
        $id, 
        $module, 
        ?AccountService $accountService = null,
        $config = []
    ) {
        $this->accountService = $accountService ?? new AccountService();
        
        $this->accountsClass = \PortalPeople\accounts\models\Accounts::class;
        $event = new \common\events\UserDataEvent(['sender' => $this->data ?? null]);
        $this->trigger(self::FIND_ACCOUNT, $event);
        if ($event->result !== null) {
            $this->accountsClass = $event->result;
        }

        /**
         * Sets the default class for account extension, then triggers the FIND_ACCOUNT_EXTENDED event.
         * 
         * - Initializes `$this->accountExtendedClass` with the default `Accounts_extended` model class.
         * - Creates a `UserDataEvent` with the current data as the sender.
         * - Triggers the `FIND_ACCOUNT_EXTENDED` event, allowing event handlers to modify the result.
         * - If the event handler sets a non-null result, updates `$this->accountExtendedClass` with the new value.
         *
         * This mechanism allows dynamic extension or replacement of the account extension class
         * via event-driven architecture, enabling customization without modifying core logic.
         */
        $this->accountExtendedClass = \PortalPeople\accounts\models\Accounts_extended::class;
        $event = new \common\events\UserDataEvent(['sender' => $this->data ?? null]);
        $this->trigger(self::FIND_ACCOUNT_EXTENDED, $event);
        if ($event->result !== null) {
            $this->accountExtendedClass = $event->result;
        }
            
        $this->viewLocation = '@backend';
        
        parent::__construct($id, $module, $config);
    }

    /**
     * {@inheritdoc}
     */
    public function behaviors(): array
    {
        return [
            'access' => [
                'class' => AccessControl::class,
                'rules' => \common\models\Permissions::getPermissions(self::class),
            ],
            'verbs' => [
                'class' => VerbFilter::class,
                'actions' => [
                    'delete' => ['POST'],
                ],
            ],
        ];
    }

    /**
     * Lists all accounts with search and filtering.
     * 
     * @return string The rendered index view
     */
    public function actionIndex(): string
    {
        $searchModel = new Accountssearch();
        $dataProvider = $searchModel->search(Yii::$app->request->queryParams);

        return $this->render('index', [
            'searchModel' => $searchModel,
            'dataProvider' => $dataProvider,
        ]);
    }

    /**
     * Displays details of a single account.
     * 
     * @param string $id The account ID
     * @return string The rendered view
     * @throws NotFoundHttpException If the account is not found
     */
    public function actionView(string $id): string
    {
        $model = $this->findModel($id);
        $linked = $this->accountService->getLinkedAccounts($model);
        
        return $this->render('view', [
            'model' => $model,
            'linked' => $linked,
        ]);
    }

    /**
     * Creates a new account.
     * 
     * @return Response|string Redirect response or rendered form
     */
    public function actionCreate(): Response|string
    {
        $accountClass = $this->accountsClass;
        $model = new $accountClass(['id' => $accountClass::Tempid()]);
        
        if ($model->load(Yii::$app->request->post())) {
            try {
                // Extract address data if provided
                $addressData = Yii::$app->request->post('Address', []);
                
                if ($this->accountService->saveWithAddress($model, $addressData)) {
                    Yii::$app->session->setFlash('success', 'Account created successfully.');
                    return $this->redirect(['view', 'id' => $model->id]);
                }
            } catch (\Throwable $e) {
                Yii::error('Failed to create account: ' . $e->getMessage(), __METHOD__);
                Yii::$app->session->setFlash('error', 'Failed to create account: ' . $e->getMessage());
            }
        }
        
        return $this->render('create', ['model' => $model]);
    }

    /**
     * Deletes an account.
     * 
     * @param string $id The account ID
     * @return Response Redirect response
     * @throws NotFoundHttpException If the account is not found
     */
    public function actionDelete(string $id): Response
    {
        $model = $this->findModel($id);
        
        try {
            // Use soft delete via service
            if ($this->accountService->deleteAccount($model)) {
                Yii::$app->session->setFlash('success', 'Account deleted successfully.');
            } else {
                Yii::$app->session->setFlash('error', 'Failed to delete account.');
            }
        } catch (\Throwable $e) {
            Yii::error('Failed to delete account: ' . $e->getMessage(), __METHOD__);
            Yii::$app->session->setFlash('error', 'Failed to delete account: ' . $e->getMessage());
        }
        
        return $this->redirect(Yii::$app->request->referrer ?? ['index']);
    }

    /**
     * Updates an existing account.
     * 
     * @param string $id The account ID
     * @return Response|string Redirect response or rendered form
     * @throws NotFoundHttpException If the account is not found
     */
    public function actionUpdate(string $id): Response|string
    {
        $model = $this->findModel($id);
        $extended = $model->extended;
        
        // Get branch data provider
        $branchSearchModel = new Accountssearch();
        $branchProvider = $branchSearchModel->relatedbranchsearch($id, Yii::$app->request->queryParams);
        
        if (Yii::$app->request->isPost) {
            $postData = Yii::$app->request->post();
            
            if ($extended->load($postData) && $extended->validate() && $model->load($postData)) {
                try {
                    // Extract address data if provided
                    $addressData = $postData['Address'] ?? [];
                    
                    if ($this->accountService->updateAccountWithAddress($model, [], $addressData)) {
                        Yii::$app->session->setFlash('success', 'Account updated successfully.');
                        return $this->redirect(['view', 'id' => $model->id]);
                    }
                } catch (\Throwable $e) {
                    Yii::error('Failed to update account: ' . $e->getMessage(), __METHOD__);
                    Yii::$app->session->setFlash('error', 'Failed to update account: ' . $e->getMessage());
                }
            }
        }
        
        return $this->render('update', [
            'model' => $model,
            'extended' => $extended,
            'branchProvider' => $branchProvider,
            'branchsearchModel' => $branchSearchModel,
        ]);
    }

    /**
     * Finds an account model by ID.
     * 
     * @param string $id The account ID
     * @return Accounts The loaded model
     * @throws NotFoundHttpException If the account is not found
     */
    protected function findModel(string $id): Accounts
    {
        $accountClass = $this->accountsClass;
        $model = $accountClass::findOne($id);
        
        if ($model === null) {
            throw new NotFoundHttpException('The requested account does not exist.');
        }
        
        return $model;
    }
    
    /**
     * API endpoint: Returns a list of accounts based on search query.
     * Used for autocomplete/select2 widgets.
     * 
     * @param string $q The search query
     * @return array JSON response with account results
     */
    public function actionCodes(string $q): array
    {
        Yii::$app->response->format = Response::FORMAT_JSON;
        
        $results = $this->accountService->searchAccounts($q);
        return $this->accountService->formatAccountResults($results);
    }

    /**
     * API endpoint: Returns a list of agent accounts based on search query.
     * Used for autocomplete/select2 widgets.
     * 
     * @param string $q The search query
     * @return array JSON response with agent results
     */
    public function actionAgents(string $q): array
    {
        Yii::$app->response->format = Response::FORMAT_JSON;
        
        $results = $this->accountService->searchAccounts($q, 'Agent');
        return $this->accountService->formatAccountResults($results);
    }
}
