<?php

namespace App\Controllers;

use App\Models\BlockchainTransactionModel;
use App\Models\MaintenanceEventModel;
use App\Models\MaintenanceFileModel;
use App\Models\MaintenanceInterventionModel;
use App\Models\MaintenanceInterventionTaskModel;
use App\Models\SiteModel;
use App\Models\TurbineModel;
use App\Models\UserModel;

class Maintenance extends BaseController
{
    private MaintenanceInterventionModel $model;
    private MaintenanceInterventionTaskModel $tasks;
    private MaintenanceEventModel $events;
    private MaintenanceFileModel $files;
    private BlockchainTransactionModel $transactions;
    private SiteModel $sites;
    private TurbineModel $turbines;
    private UserModel $users;

    public function __construct()
    {
        $this->model = new MaintenanceInterventionModel();
        $this->tasks = new MaintenanceInterventionTaskModel();
        $this->events = new MaintenanceEventModel();
        $this->files = new MaintenanceFileModel();
        $this->transactions = new BlockchainTransactionModel();
        $this->sites = new SiteModel();
        $this->turbines = new TurbineModel();
        $this->users = new UserModel();
    }

    public function index()
    {
        if ($redirect = $this->requireOrganizationContext()) {
            return $redirect;
        }

        $query = $this->model->withRelations();

        if (! $this->isPlatformAdmin()) {
            $query->where('maintenance_interventions.organization_id', (int) $this->currentOrganizationId());
        }

        return view('maintenance/index', [
            'title' => 'Maintenance',
            'active' => 'maintenance',
            'interventions' => $query->orderBy('maintenance_interventions.created_at', 'DESC')->findAll(),
        ]);
    }

    public function new()
    {
        if ($redirect = $this->requireOrganizationContext()) {
            return $redirect;
        }

        if (! $this->canManageOperations()) {
            return redirect()->to(site_url('maintenance'))->with('error', 'Nao tens permissao para criar intervencoes.');
        }

        return view('maintenance/form', [
            'title' => 'Nova intervencao',
            'active' => 'maintenance',
            'intervention' => null,
            'sites' => $this->siteOptions(),
            'turbines' => $this->turbineOptions(),
            'users' => $this->userOptions(),
            'action' => site_url('maintenance'),
        ]);
    }

    public function create()
    {
        if (! $this->canManageOperations()) {
            return redirect()->to(site_url('maintenance'))->with('error', 'Nao tens permissao para criar intervencoes.');
        }

        $data = $this->collectData();
        $data['intervention_code'] = $data['intervention_code'] ?: $this->nextInterventionCode();

        if (! $this->validateData($data, $this->rules())) {
            return redirect()->back()->withInput()->with('error', 'Verifica os campos obrigatorios da intervencao.');
        }

        if (! $this->siteBelongsToCurrentOrganization((int) $data['site_id'])) {
            return redirect()->back()->withInput()->with('error', 'O site selecionado nao pertence a organizacao ativa.');
        }

        if ($data['turbine_id'] !== null && ! $this->turbineBelongsToSite((int) $data['turbine_id'], (int) $data['site_id'])) {
            return redirect()->back()->withInput()->with('error', 'A turbina selecionada nao pertence ao site escolhido.');
        }

        if ($this->interventionCodeExists((int) $data['organization_id'], $data['intervention_code'])) {
            return redirect()->back()->withInput()->with('error', 'Ja existe uma intervencao com esse codigo nesta organizacao.');
        }

        $this->model->insert($data);

        return redirect()->to(site_url('maintenance'))->with('success', 'Intervencao criada com sucesso.');
    }

    public function show(string $code)
    {
        if ($redirect = $this->requireOrganizationContext()) {
            return $redirect;
        }

        $intervention = $this->findIntervention($code);
        if (! $intervention) {
            throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound('Intervencao nao encontrada.');
        }

        return view('maintenance/show', [
            'title' => 'Intervencao ' . $intervention['intervention_code'],
            'active' => 'maintenance',
            'intervention' => $intervention,
            'tasks' => $this->tasks->forIntervention((int) $intervention['id']),
            'events' => $this->events->forIntervention((int) $intervention['id']),
            'files' => $this->files->forIntervention((int) $intervention['id']),
            'transactions' => $this->transactions->forIntervention((int) $intervention['id']),
        ]);
    }

    public function edit(string $code)
    {
        $intervention = $this->findIntervention($code);
        if (! $intervention) {
            throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound('Intervencao nao encontrada.');
        }

        if (! $this->canManageOperations()) {
            return redirect()->to(site_url('maintenance'))->with('error', 'Nao tens permissao para editar intervencoes.');
        }

        return view('maintenance/form', [
            'title' => 'Editar intervencao',
            'active' => 'maintenance',
            'intervention' => $intervention,
            'sites' => $this->siteOptions(),
            'turbines' => $this->turbineOptions(),
            'users' => $this->userOptions(),
            'action' => site_url('maintenance/' . rawurlencode($intervention['intervention_code'])),
        ]);
    }

    public function update(string $code)
    {
        $intervention = $this->findIntervention($code);
        if (! $intervention) {
            throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound('Intervencao nao encontrada.');
        }

        if (! $this->canManageOperations()) {
            return redirect()->to(site_url('maintenance'))->with('error', 'Nao tens permissao para editar intervencoes.');
        }

        $data = $this->collectData();
        $data['intervention_code'] = $data['intervention_code'] ?: $intervention['intervention_code'];

        if (! $this->validateData($data, $this->rules())) {
            return redirect()->back()->withInput()->with('error', 'Verifica os campos obrigatorios da intervencao.');
        }

        if (! $this->siteBelongsToCurrentOrganization((int) $data['site_id'])) {
            return redirect()->back()->withInput()->with('error', 'O site selecionado nao pertence a organizacao ativa.');
        }

        if ($data['turbine_id'] !== null && ! $this->turbineBelongsToSite((int) $data['turbine_id'], (int) $data['site_id'])) {
            return redirect()->back()->withInput()->with('error', 'A turbina selecionada nao pertence ao site escolhido.');
        }

        if ($this->interventionCodeExists((int) $data['organization_id'], $data['intervention_code'], (int) $intervention['id'])) {
            return redirect()->back()->withInput()->with('error', 'Ja existe uma intervencao com esse codigo nesta organizacao.');
        }

        $this->model->update((int) $intervention['id'], $data);

        return redirect()->to(site_url('maintenance/' . rawurlencode($data['intervention_code'])))->with('success', 'Intervencao atualizada com sucesso.');
    }

    public function anchorBlockchain(string $code)
    {
        if ($redirect = $this->requireOrganizationContext()) {
            return $redirect;
        }

        $intervention = $this->findIntervention($code);
        if (! $intervention) {
            return redirect()->to(site_url('maintenance'))->with('error', 'Intervencao nao encontrada.');
        }

        if (! $this->canManageOperations()) {
            return redirect()
                ->to(site_url('maintenance/' . rawurlencode($code)))
                ->with('error', 'Nao tens permissao para ancorar esta intervencao na blockchain.');
        }

        if ($this->transactions->where('maintenance_intervention_id', (int) $intervention['id'])->countAllResults() > 0) {
            return redirect()
                ->to(site_url('maintenance/' . rawurlencode($intervention['intervention_code'])))
                ->with('error', 'Esta intervencao ja tem uma transacao blockchain associada.');
        }

        $anchoredHash = $this->buildAnchoredHash($intervention);
        $transactionHash = '0x' . substr(
            hash('sha256', implode('|', [
                'quantum-maintenance-anchor',
                $intervention['intervention_code'],
                $anchoredHash,
                (string) microtime(true),
                (string) random_int(1000, 999999),
            ])),
            0,
            64
        );

        $this->transactions->insert([
            'maintenance_intervention_id' => (int) $intervention['id'],
            'network' => 'polygon-pos',
            'transaction_hash' => $transactionHash,
            'contract_address' => '0x' . substr(hash('sha256', 'quantum-maintenance-record-demo'), 0, 40),
            'block_number' => 56124000 + random_int(1, 999999),
            'status' => 'confirmed',
            'anchor_type' => 'intervention',
            'anchored_hash' => $anchoredHash,
        ]);

        $this->events->insert([
            'maintenance_intervention_id' => (int) $intervention['id'],
            'event_type' => 'blockchain_anchor_confirmed',
            'event_message' => 'Registo blockchain confirmado para a intervencao tecnica.',
            'event_data' => json_encode([
                'channel' => 'backoffice',
                'network' => 'polygon-pos',
                'transaction_hash' => $transactionHash,
                'anchored_hash' => $anchoredHash,
            ], JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE),
            'happened_at' => date('Y-m-d H:i:s'),
            'created_by_user_id' => $this->authUser()['id'] ?? null,
        ]);

        return redirect()
            ->to(site_url('maintenance/' . rawurlencode($intervention['intervention_code'])))
            ->with('success', 'Intervencao ancorada na blockchain com sucesso.');
    }

    public function delete(string $code)
    {
        $intervention = $this->findIntervention($code);
        if (! $intervention) {
            return redirect()->to(site_url('maintenance'))->with('error', 'Intervencao nao encontrada.');
        }

        if (! $this->canManageOperations()) {
            return redirect()->to(site_url('maintenance'))->with('error', 'Nao tens permissao para remover intervencoes.');
        }

        $this->model->delete((int) $intervention['id']);

        return redirect()->to(site_url('maintenance'))->with('success', 'Intervencao removida com sucesso.');
    }

    private function collectData(): array
    {
        $organizationId = $this->isPlatformAdmin()
            ? (int) $this->request->getPost('organization_id')
            : (int) $this->currentOrganizationId();

        return [
            'organization_id' => $organizationId,
            'site_id' => (int) $this->request->getPost('site_id'),
            'turbine_id' => $this->nullableInt($this->request->getPost('turbine_id')),
            'intervention_code' => trim((string) $this->request->getPost('intervention_code')),
            'title' => trim((string) $this->request->getPost('title')),
            'description' => trim((string) $this->request->getPost('description')) ?: null,
            'intervention_type' => trim((string) $this->request->getPost('intervention_type')) ?: 'preventive',
            'maintenance_mode' => trim((string) $this->request->getPost('maintenance_mode')) ?: 'scheduled',
            'priority' => trim((string) $this->request->getPost('priority')) ?: 'medium',
            'status' => trim((string) $this->request->getPost('status')) ?: 'planned',
            'reported_at' => trim((string) $this->request->getPost('reported_at')) ?: null,
            'scheduled_start_at' => trim((string) $this->request->getPost('scheduled_start_at')) ?: null,
            'scheduled_end_at' => trim((string) $this->request->getPost('scheduled_end_at')) ?: null,
            'started_at' => trim((string) $this->request->getPost('started_at')) ?: null,
            'completed_at' => trim((string) $this->request->getPost('completed_at')) ?: null,
            'created_by_user_id' => $this->authUser()['id'] ?? null,
            'assigned_to_user_id' => $this->nullableInt($this->request->getPost('assigned_to_user_id')),
            'external_reference' => trim((string) $this->request->getPost('external_reference')) ?: null,
        ];
    }

    private function rules(): array
    {
        return [
            'organization_id' => 'required|integer',
            'site_id' => 'required|integer',
            'turbine_id' => 'permit_empty|integer',
            'intervention_code' => 'required|max_length[120]',
            'title' => 'required|min_length[3]|max_length[160]',
            'description' => 'permit_empty',
            'intervention_type' => 'required|in_list[preventive,corrective,inspection,audit,emergency]',
            'maintenance_mode' => 'required|in_list[scheduled,corrective,condition_based,outage]',
            'priority' => 'required|in_list[low,medium,high,critical]',
            'status' => 'required|in_list[planned,in_progress,completed,cancelled,on_hold]',
            'reported_at' => 'permit_empty',
            'scheduled_start_at' => 'permit_empty',
            'scheduled_end_at' => 'permit_empty',
            'started_at' => 'permit_empty',
            'completed_at' => 'permit_empty',
            'assigned_to_user_id' => 'permit_empty|integer',
            'external_reference' => 'permit_empty|max_length[120]',
        ];
    }

    private function siteOptions(): array
    {
        $query = $this->sites->withOrganization();

        if (! $this->isPlatformAdmin()) {
            $query->where('sites.organization_id', (int) $this->currentOrganizationId());
        }

        return $query->orderBy('sites.name', 'ASC')->findAll();
    }

    private function turbineOptions(): array
    {
        $query = $this->turbines->withRelations();

        if (! $this->isPlatformAdmin()) {
            $query->where('sites.organization_id', (int) $this->currentOrganizationId());
        }

        return $query->orderBy('sites.name', 'ASC')->orderBy('turbines.turbine_code', 'ASC')->findAll();
    }

    private function userOptions(): array
    {
        $builder = db_connect()->table('users')
            ->select('users.id, users.name, users.email, organization_memberships.role')
            ->join('organization_memberships', 'organization_memberships.user_id = users.id')
            ->where('organization_memberships.organization_id', (int) $this->currentOrganizationId())
            ->where('organization_memberships.status', 'active')
            ->orderBy('users.name', 'ASC');

        return $builder->get()->getResultArray();
    }

    private function findIntervention(string $code): ?array
    {
        $query = $this->model->withRelations()
            ->where('maintenance_interventions.intervention_code', $code);

        if (! $this->isPlatformAdmin()) {
            $query->where('maintenance_interventions.organization_id', (int) $this->currentOrganizationId());
        }

        return $query->first();
    }

    private function siteBelongsToCurrentOrganization(int $siteId): bool
    {
        if ($this->isPlatformAdmin()) {
            return true;
        }

        $site = $this->sites->find($siteId);

        return $site !== null && (int) $site['organization_id'] === (int) $this->currentOrganizationId();
    }

    private function turbineBelongsToSite(int $turbineId, int $siteId): bool
    {
        $turbine = $this->turbines->find($turbineId);

        return $turbine !== null && (int) $turbine['site_id'] === $siteId;
    }

    private function interventionCodeExists(int $organizationId, string $code, ?int $ignoreId = null): bool
    {
        $builder = $this->model
            ->where('organization_id', $organizationId)
            ->where('intervention_code', $code);

        if ($ignoreId !== null) {
            $builder->where('id !=', $ignoreId);
        }

        return $builder->first() !== null;
    }

    private function nextInterventionCode(): string
    {
        $year = date('Y');
        $prefix = 'MI-' . $year . '-';

        $builder = $this->model
            ->select('intervention_code')
            ->like('intervention_code', $prefix, 'after')
            ->orderBy('id', 'DESC');

        if (! $this->isPlatformAdmin()) {
            $builder->where('organization_id', (int) $this->currentOrganizationId());
        }

        $last = $builder->first();
        if (! $last || empty($last['intervention_code'])) {
            return $prefix . '0001';
        }

        $lastNumber = (int) substr((string) $last['intervention_code'], -4);

        return $prefix . str_pad((string) ($lastNumber + 1), 4, '0', STR_PAD_LEFT);
    }

    private function buildAnchoredHash(array $intervention): string
    {
        $payload = [
            'intervention_code' => $intervention['intervention_code'] ?? null,
            'organization_id' => (int) ($intervention['organization_id'] ?? 0),
            'site_id' => (int) ($intervention['site_id'] ?? 0),
            'turbine_id' => $intervention['turbine_id'] !== null ? (int) $intervention['turbine_id'] : null,
            'title' => $intervention['title'] ?? null,
            'description' => $intervention['description'] ?? null,
            'intervention_type' => $intervention['intervention_type'] ?? null,
            'maintenance_mode' => $intervention['maintenance_mode'] ?? null,
            'priority' => $intervention['priority'] ?? null,
            'status' => $intervention['status'] ?? null,
            'reported_at' => $intervention['reported_at'] ?? null,
            'scheduled_start_at' => $intervention['scheduled_start_at'] ?? null,
            'scheduled_end_at' => $intervention['scheduled_end_at'] ?? null,
            'started_at' => $intervention['started_at'] ?? null,
            'completed_at' => $intervention['completed_at'] ?? null,
            'external_reference' => $intervention['external_reference'] ?? null,
        ];

        return hash('sha256', json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE));
    }

    private function nullableInt($value): ?int
    {
        $value = trim((string) $value);

        return $value === '' ? null : (int) $value;
    }
}
