<?php
namespace App\Controllers;

use App\Core\Auth;
use App\Core\Controller;
use App\Core\Csrf;
use App\Core\Formatter;
use App\Core\Permission;
use App\Core\Database;
use App\Models\Client;
use App\Models\EnrollmentRequest;
use App\Services\AdherentAnnualService;

final class ClientController extends Controller
{
    public function index(): void
    {
        Auth::requireLogin();
        Permission::requireAny('clients.view', 'family.profile.view');

        $query = trim($_GET['q'] ?? '');
        $isFamilyPortal = Permission::isFamilyPortalIdentity();
        $ownClientId = $isFamilyPortal
            ? Permission::familyPortalClientId()
            : null;
        if ($isFamilyPortal && $ownClientId === null) {
            http_response_code(403);
            exit('Aucun dossier adhérent n’est rattaché à ce compte Famille.');
        }
        if ($isFamilyPortal) {
            $this->redirect('/mon-espace/dossier/identite');
            return;
        }

        $this->view('clients/index', [
            'pageTitle' => 'Adhérents & familles',
            'clients' => Client::search($query, $ownClientId),
            'query' => $query,
            'annualDashboard' => AdherentAnnualService::dashboard((int)(Auth::user()['organisation_id'] ?? 1)),
        ]);
    }

    public function create(): void
    {
        Auth::requireLogin();
        $this->denyFamilyPortalAdministration();
        Permission::require('clients.create');

        $this->view('clients/form', [
            'pageTitle' => 'Nouvel adhérent',
        ]);
    }

    public function store(): void
    {
        Auth::requireLogin();
        $this->denyFamilyPortalAdministration();
        Permission::require('clients.create');
        Csrf::verify($_POST['_csrf'] ?? null);

        [$data, $errors] = $this->validateAdherent();
        $createFamilyAccount = isset($_POST['create_family_account']);
        if ($createFamilyAccount) {
            if ($data['email'] === '') {
                $errors[] = 'L’adresse e-mail est obligatoire pour créer l’espace Famille.';
            }
            if ($data['email'] !== '' && Client::accountEmailExists($data['email'])) {
                $errors[] = 'Un compte utilise déjà cette adresse e-mail. Le rattachement doit être réalisé depuis le Centre des comptes portail.';
            }
        }

        if (Client::duplicateExists(
            $data['nom'],
            $data['prenom'],
            $data['date_naissance']
        )) {
            $errors[] = 'Un adhérent avec le même nom, prénom et date de naissance existe déjà.';
        }

        if ($errors) {
            $_SESSION['form_errors'] = $errors;
            $_SESSION['old'] = $_POST;
            $this->redirect('/clients/nouveau');
        }

        $data['organisation_id'] = (int)(Auth::user()['organisation_id'] ?? 1);

        $data['create_family_account'] = $createFamilyAccount;
        $result = Client::create($data, (int)Auth::id());
        $id = (int)$result['client_id'];
        if (!empty($result['invitation'])) {
            $sent = (new \App\Services\PortalAccountService())->sendInvitation($result['invitation']);
            $_SESSION[$sent ? 'success' : 'warning'] = $sent
                ? 'L’adhérent, sa famille et son espace sécurisé ont été créés. L’invitation a été envoyée.'
                : 'L’adhérent, sa famille et son espace sécurisé ont été créés. Le serveur de messagerie a refusé l’envoi : renvoyez le lien depuis le Centre des comptes portail.';
        } else {
            $_SESSION['success'] = 'L’adhérent et son dossier familial ont été créés.';
        }

        $created = Client::find($id);
        $this->redirect(Client::publicUrl($created));
    }

    public function show(): void
    {
        Auth::requireLogin();
        Permission::requireAny('clients.view', 'family.profile.view');

        if (Permission::isFamilyPortalIdentity()) {
            $this->redirect('/mon-espace/dossier/identite');
        }
        if (isset($_GET['ref']) || isset($_GET['id'])) {
            http_response_code(404);
            exit('Dossier introuvable.');
        }
        $client = Client::findByPublicToken((string)($_GET['dossier'] ?? ''));
        $id = (int)($client['id'] ?? 0);

        if (!$client) {
            http_response_code(404);
            exit('Adhérent introuvable.');
        }
        $this->assertClientAccess($id);

        $familyInvoices=[];
        $familyDocuments=[];
        if (Permission::isFamilyPortalIdentity()) {
            $stmt=Database::connection()->prepare(
                "SELECT i.*,
                        COALESCE((SELECT SUM(p.amount) FROM cda_payments p WHERE p.invoice_id=i.id),0) AS paid_amount
                 FROM cda_invoices i
                 WHERE i.client_id=:client_id
                   AND i.organisation_id=:organisation_id
                   AND i.deleted_at IS NULL
                   AND i.status IN ('issued','part_paid','paid')
                 ORDER BY i.created_at DESC,i.id DESC"
            );
            $stmt->execute(['client_id'=>$id,'organisation_id'=>(int)$client['organisation_id']]);
            $familyInvoices=$stmt->fetchAll()?:[];

            $documents=Database::connection()->prepare(
                "SELECT d.*,m.prenom AS membre_prenom,m.nom AS membre_nom
                 FROM membre_documents d
                 INNER JOIN membres_famille m ON m.id=d.membre_id
                 WHERE d.famille_id=:famille_id
                   AND d.organisation_id=:organisation_id
                   AND d.deleted_at IS NULL
                   AND d.statut<>'brouillon'
                 ORDER BY d.created_at DESC,d.id DESC"
            );
            $documents->execute([
                'famille_id'=>(int)$client['famille_id'],
                'organisation_id'=>(int)$client['organisation_id'],
            ]);
            $familyDocuments=$documents->fetchAll()?:[];
        }

        $this->view('clients/show', [
            'pageTitle' => 'Dossier adhérent',
            'client' => $client,
            'members' => Client::members($id),
            'annualFiles' => AdherentAnnualService::annualFiles($id),
            'activeYear' => AdherentAnnualService::activeYear((int)$client['organisation_id']),
            'timeline' => AdherentAnnualService::timeline($id),
            'enrollmentRequests' => EnrollmentRequest::forClient($id),
            'familyInvoices' => $familyInvoices,
            'familyDocuments' => $familyDocuments,
        ]);
    }

    public function familyDossier(): void
    {
        Auth::requireLogin();
        Permission::requireAny('clients.view', 'family.profile.view');
        if (!Permission::isFamilyPortalIdentity()) {
            http_response_code(404);
            exit('Dossier introuvable.');
        }
        $clientId = Permission::familyPortalClientId();
        if (!$clientId) {
            http_response_code(404);
            exit('Dossier introuvable.');
        }
        $path = trim((string)(parse_url($_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH) ?? ''), '/');
        $tab = (string)basename($path);
        $allowed = ['identite','famille','annees','documents','factures','historique'];
        if (!in_array($tab, $allowed, true)) {
            $this->redirect('/mon-espace/dossier/identite');
        }
        $_GET['onglet'] = $tab === 'factures' ? 'facturation' : $tab;
        $client = Client::find((int)$clientId);
        if (!$client) {
            http_response_code(404);
            exit('Dossier introuvable.');
        }
        $this->renderClient($client, $tab === 'factures' ? 'facturation' : $tab);
    }

    private function renderClient(array $client, string $activeFamilyTab = 'identite'): void
    {
        $id = (int)$client['id'];
        $stmt=Database::connection()->prepare(
            "SELECT i.*,
                    COALESCE((SELECT SUM(p.amount) FROM cda_payments p WHERE p.invoice_id=i.id),0) AS paid_amount
             FROM cda_invoices i
             WHERE i.client_id=:client_id AND i.organisation_id=:organisation_id
               AND i.deleted_at IS NULL AND i.status IN ('issued','part_paid','paid')
             ORDER BY i.created_at DESC,i.id DESC"
        );
        $stmt->execute(['client_id'=>$id,'organisation_id'=>(int)$client['organisation_id']]);
        $documents=Database::connection()->prepare(
            "SELECT d.*,m.prenom AS membre_prenom,m.nom AS membre_nom
             FROM membre_documents d
             INNER JOIN membres_famille m ON m.id=d.membre_id
             WHERE d.famille_id=:famille_id AND d.organisation_id=:organisation_id
               AND d.deleted_at IS NULL AND d.statut<>'brouillon'
             ORDER BY d.created_at DESC,d.id DESC"
        );
        $documents->execute([
            'famille_id'=>(int)$client['famille_id'],
            'organisation_id'=>(int)$client['organisation_id'],
        ]);
        $this->view('clients/show',[
            'pageTitle'=>'Mon dossier',
            'client'=>$client,
            'members'=>Client::members($id),
            'annualFiles'=>AdherentAnnualService::annualFiles($id),
            'activeYear'=>AdherentAnnualService::activeYear((int)$client['organisation_id']),
            'timeline'=>AdherentAnnualService::timeline($id),
            'enrollmentRequests'=>EnrollmentRequest::forClient($id),
            'familyInvoices'=>$stmt->fetchAll()?:[],
            'familyDocuments'=>$documents->fetchAll()?:[],
            'activeFamilyTab'=>$activeFamilyTab,
        ]);
    }

    public function edit(): void
    {
        Auth::requireLogin();
        $this->denyFamilyPortalAdministration();
        Permission::requireAny('clients.edit', 'clients.create', 'family.profile.edit');
        $reference = trim((string)($_GET['ref'] ?? ''));
        $client = $reference !== ''
            ? Client::findByReference($reference)
            : Client::find((int)($_GET['id'] ?? 0));
        if (!$client) { http_response_code(404); exit('Adhérent introuvable.'); }
        $this->assertClientAccess((int)$client['id']);
        $this->view('clients/form', ['pageTitle'=>'Modifier l’adhérent','client'=>$client,'editing'=>true]);
    }

    public function update(): void
    {
        Auth::requireLogin();
        $this->denyFamilyPortalAdministration();
        Permission::requireAny('clients.edit', 'clients.create', 'family.profile.edit');
        Csrf::verify($_POST['_csrf'] ?? null);
        $id = (int)($_POST['id'] ?? 0);
        $client = Client::find($id);
        if (!$client) { http_response_code(404); exit('Adhérent introuvable.'); }
        $this->assertClientAccess($id);
        [$data, $errors] = $this->validateAdherent();
        if ($errors) {
            $_SESSION['form_errors']=$errors; $_SESSION['old']=$_POST;
            $this->redirect('/clients/modifier?id='.$id);
        }
        Client::update($id, $data, (int)Auth::id());
        $_SESSION['success']='Les informations ont été corrigées. Le code adhérent permanent est conservé.';
        $this->redirect(Client::publicUrl($client));
    }

    public function createMember(): void
    {
        Auth::requireLogin();
        $this->denyFamilyPortalAdministration();
        Permission::require('membres.manage');

        $publicToken = trim((string)($_GET['dossier'] ?? ''));
        $resolved = $publicToken !== '' ? Client::findByPublicToken($publicToken) : null;
        $id = (int)($resolved['id'] ?? ($_GET['client_id'] ?? 0));

        if (!Client::find($id)) {
            http_response_code(404);
            exit('Adhérent introuvable.');
        }
        $this->assertClientAccess($id);

        $this->view('clients/member_form', [
            'pageTitle' => 'Nouveau membre de la famille',
            'clientId' => $id,
        ]);
    }

    public function storeMember(): void
    {
        Auth::requireLogin();
        $this->denyFamilyPortalAdministration();
        Permission::require('membres.manage');
        Csrf::verify($_POST['_csrf'] ?? null);

        $clientId = (int)($_GET['client_id'] ?? 0);
        $this->assertClientAccess($clientId);
        [$data, $errors] = $this->validateMember();

        if ($errors) {
            $_SESSION['form_errors'] = $errors;
            $_SESSION['old'] = $_POST;
            $this->redirect('/clients/membres/nouveau?client_id=' . $clientId);
        }

        Client::addMember($clientId, $data);

        $_SESSION['success'] =
            'Le membre a été rattaché à la famille de l’adhérent.';

        $target = Client::find($clientId);
        $this->redirect(Client::publicUrl($target));
    }


    public function activateAnnualFile(): void
    {
        Auth::requireLogin();
        $this->denyFamilyPortalAdministration();
        Permission::require('clients.create');
        Csrf::verify($_POST['_csrf'] ?? null);

        $clientId = (int)($_POST['client_id'] ?? 0);
        $client = Client::find($clientId);
        if (!$client) {
            http_response_code(404);
            exit('Adhérent introuvable.');
        }

        $decision = (string)($_POST['annual_decision'] ?? '');
        if (!in_array($decision, ['pending', 'activate'], true)) {
            $_SESSION['form_errors'] = ['Indiquez d’abord si l’année doit être activée ou rester en demande uniquement.'];
            $this->redirect(Client::publicUrl($client, 'annees') . '#dossiers-annuels');
        }

        if ($decision === 'pending') {
            $_SESSION['success'] = 'L’année reste non activée : la demande peut être suivie sans adhésion ni facturation.';
            $this->redirect(Client::publicUrl($client, 'annees') . '#dossiers-annuels');
        }

        try {
            $result = AdherentAnnualService::activate(
                $clientId,
                (int)$client['organisation_id'],
                (int)Auth::id()
            );
            if (!empty($result['newly_activated'])) {
                $_SESSION['success'] = 'Dossier ' . (string)$result['annee']['libelle'] . ' activé. L’adhésion a été transmise au module Facturation avec le statut « À facturer ».';
            } elseif (!empty($result['billing_created'])) {
                $_SESSION['success'] = 'L’année était déjà active. L’adhésion manquante a été transmise une seule fois au module Facturation.';
            } else {
                $_SESSION['success'] = 'L’année ' . (string)$result['annee']['libelle'] . ' est déjà active. Aucune nouvelle ligne n’a été créée.';
            }
        } catch (\Throwable $error) {
            $_SESSION['form_errors'] = [$error->getMessage()];
        }

        $target = Client::find($clientId);
        $this->redirect(Client::publicUrl($target, 'annees') . '#dossiers-annuels');
    }

    private function assertClientAccess(int $clientId): void
    {
        if (!Permission::canAccessClient($clientId)) {
            http_response_code(403);
            exit('Ce dossier familial ne vous est pas accessible.');
        }
    }

    private function denyFamilyPortalAdministration(): void
    {
        if (Permission::isFamilyPortalIdentity()) {
            http_response_code(403);
            exit('Cette action de gestion interne n’est pas disponible depuis un compte Famille.');
        }
    }

    private function validateAdherent(): array
    {
        $errors = [];

        $nom = Formatter::lastName($_POST['nom'] ?? '');
        $prenom = Formatter::firstName($_POST['prenom'] ?? '');
        $email = Formatter::email($_POST['email'] ?? '');
        $dateNaissance = trim($_POST['date_naissance'] ?? '');

        if ($nom === '') {
            $errors[] = 'Le nom de l’adhérent est obligatoire.';
        }

        if ($prenom === '') {
            $errors[] = 'Le prénom de l’adhérent est obligatoire.';
        }

        if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $errors[] = 'L’adresse e-mail n’est pas valide.';
        }

        if (!Formatter::validPhone($_POST['telephone_principal'] ?? '')) {
            $errors[] = 'Le téléphone principal n’est pas valide.';
        }

        if (!Formatter::validPhone($_POST['telephone_secondaire'] ?? '')) {
            $errors[] = 'Le téléphone secondaire n’est pas valide.';
        }

        return [[
            'civilite' => $_POST['civilite'] ?? 'Non renseigne',
            'nom' => $nom,
            'prenom' => $prenom,
            'date_naissance' => $dateNaissance,
            'email' => $email,
            'telephone_principal' =>
                Formatter::phone($_POST['telephone_principal'] ?? ''),
            'telephone_secondaire' =>
                Formatter::phone($_POST['telephone_secondaire'] ?? ''),
            'adresse_ligne1' => trim($_POST['adresse_ligne1'] ?? ''),
            'adresse_ligne2' => trim($_POST['adresse_ligne2'] ?? ''),
            'code_postal' => Formatter::postalCode($_POST['code_postal'] ?? ''),
            'ville' => Formatter::city($_POST['ville'] ?? ''),
            'pays' => trim($_POST['pays'] ?? 'France'),
            'rgpd_accepte' => isset($_POST['rgpd_accepte']) ? 1 : 0,
            'client_participant' =>
                isset($_POST['client_participant']) ? 1 : 0,
            'notes' => trim($_POST['notes'] ?? ''),
        ], $errors];
    }

    private function validateMember(): array
    {
        $errors = [];

        $nom = Formatter::lastName($_POST['nom'] ?? '');
        $prenom = Formatter::firstName($_POST['prenom'] ?? '');
        $dateNaissance = trim($_POST['date_naissance'] ?? '');
        $email = Formatter::email($_POST['email'] ?? '');

        if ($nom === '') {
            $errors[] = 'Le nom du membre est obligatoire.';
        }

        if ($prenom === '') {
            $errors[] = 'Le prénom du membre est obligatoire.';
        }

        if ($dateNaissance === '') {
            $errors[] = 'La date de naissance du membre est obligatoire.';
        }

        if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $errors[] = 'L’adresse e-mail n’est pas valide.';
        }

        if (!Formatter::validPhone($_POST['telephone_principal'] ?? '')) {
            $errors[] = 'Le téléphone principal n’est pas valide.';
        }

        if (!Formatter::validPhone($_POST['telephone_secondaire'] ?? '')) {
            $errors[] = 'Le téléphone secondaire n’est pas valide.';
        }

        return [[
            'civilite' => $_POST['civilite'] ?? 'Non renseigne',
            'nom' => $nom,
            'prenom' => $prenom,
            'date_naissance' => $dateNaissance,
            'lien_familial' => $_POST['lien_familial'] ?? 'autre',
            'email' => $email,
            'telephone_principal' =>
                Formatter::phone($_POST['telephone_principal'] ?? ''),
            'telephone_secondaire' =>
                Formatter::phone($_POST['telephone_secondaire'] ?? ''),
            'participant_ateliers' =>
                isset($_POST['participant_ateliers']) ? 1 : 0,
            'responsable_legal' =>
                isset($_POST['responsable_legal']) ? 1 : 0,
            'autorite_parentale' =>
                isset($_POST['autorite_parentale']) ? 1 : 0,
            'payeur' => isset($_POST['payeur']) ? 1 : 0,
            'recoit_emails' => isset($_POST['recoit_emails']) ? 1 : 0,
            'recoit_sms' => isset($_POST['recoit_sms']) ? 1 : 0,
            'recoit_factures' =>
                isset($_POST['recoit_factures']) ? 1 : 0,
            'droit_image' =>
                $_POST['droit_image'] ?? 'non_renseigne',
            'rgpd_accepte' =>
                isset($_POST['rgpd_accepte']) ? 1 : 0,
            'notes' => trim($_POST['notes'] ?? ''),
        ], $errors];
    }
}
