<?php

namespace App\Controllers\Api;

use App\Models\MemberInvitationModel;
use App\Models\MemberKinModel;
use App\Models\MemberModel;

class BeneficiariesController extends ApiController
{
    protected $memberKinModel;
    protected $memberModel;
    protected $memberInvitationModel;
    protected array $adultChildRelationships = ['son', 'daughter'];

    public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger)
    {
        parent::initController($request, $response, $logger);
        
        try {
            $this->memberKinModel = new MemberKinModel();
            $this->memberModel = new MemberModel();
            $this->memberInvitationModel = new MemberInvitationModel();
        } catch (\Exception $e) {
            log_message('error', 'BeneficiariesController init error: ' . $e->getMessage());
        }
    }

    public function getByMember($memberId)
    {
        if (!$this->isAuthenticated()) {
            return $this->respond(['error' => 'Unauthorized'], 401);
        }

        $member = $this->memberModel->find($memberId);
        if (!$member) {
            return $this->respondNotFound();
        }

        $beneficiaries = $this->memberKinModel->getByMember($memberId);
        $installmentOneBeneficiary = $this->memberKinModel->getSelectedInstallmentBeneficiaryByMember($memberId, 1);
        $installmentTwoBeneficiary = $this->memberKinModel->getSelectedInstallmentBeneficiaryByMember($memberId, 2);

        return $this->respond([
            'beneficiaries' => $beneficiaries,
            'selected_installment_one_beneficiary' => $installmentOneBeneficiary,
            'selected_installment_two_beneficiary' => $installmentTwoBeneficiary,
            'total_percentage' => $this->memberKinModel->getTotalBenefitsByMember($memberId)
        ]);
    }

    private function getPublicMemberByToken(string $token): ?array
    {
        $invitation = $this->memberInvitationModel->getValidInvitationByToken($token, 'public_update');
        if (!$invitation) {
            return null;
        }

        return $this->memberModel->find($invitation['member_id']);
    }

    public function publicByToken($token)
    {
        $member = $this->getPublicMemberByToken($token);
        if (!$member) {
            return $this->respond(['error' => 'Public update link is invalid or expired.'], 404);
        }

        $beneficiaries = $this->memberKinModel->getByMember($member['id']);
        $installmentOneBeneficiary = $this->memberKinModel->getSelectedInstallmentBeneficiaryByMember($member['id'], 1);
        $installmentTwoBeneficiary = $this->memberKinModel->getSelectedInstallmentBeneficiaryByMember($member['id'], 2);

        return $this->respond([
            'member' => $member,
            'beneficiaries' => $beneficiaries,
            'selected_installment_one_beneficiary' => $installmentOneBeneficiary,
            'selected_installment_two_beneficiary' => $installmentTwoBeneficiary,
            'total_percentage' => $this->memberKinModel->getTotalBenefitsByMember($member['id']),
        ]);
    }

    public function publicCreate()
    {
        if (!$this->request->is('post')) {
            return $this->respondBadRequest('Invalid request method');
        }

        $data = $this->request->getJSON();
        if (!isset($data->token)) {
            return $this->respondValidationError(['token' => 'Public update token is required']);
        }

        $member = $this->getPublicMemberByToken($data->token);
        if (!$member) {
            return $this->respond(['error' => 'Public update link is invalid or expired.'], 404);
        }

        $validationError = $this->validateKinAgeRule($data);
        if ($validationError !== null) {
            return $this->respondBadRequest($validationError);
        }

        $beneficiaryData = [
            'member_id' => $member['id'],
            'full_name' => $data->full_name,
            'gender' => $data->gender ?? null,
            'relationship' => $data->relationship,
            'benefits' => $data->benefits ?? 0,
            'installment_one_amount' => $data->installment_one_amount ?? 3000000,
            'installment_one_selected' => 0,
            'installment_two_amount' => $data->installment_two_amount ?? 3000000,
            'installment_two_selected' => 0,
            'phone' => $data->phone ?? null,
            'email' => $data->email ?? null,
            'address' => $data->address ?? null,
            'date_of_birth' => $data->date_of_birth ?? null,
        ];

        if ($this->memberKinModel->insert($beneficiaryData)) {
            $beneficiaryId = $this->memberKinModel->insertID();
            $this->logAudit('create', 'beneficiary', $beneficiaryId, null, $beneficiaryData);
            return $this->respondCreated(['beneficiaryId' => $beneficiaryId, 'message' => 'Beneficiary added successfully']);
        }

        return $this->respondBadRequest('Failed to add beneficiary: ' . implode(', ', $this->memberKinModel->errors()));
    }

    public function create()
    {
        if (!$this->isAuthenticated()) {
            return $this->respond(['error' => 'Unauthorized'], 401);
        }

        if (!$this->request->is('post')) {
            return $this->respondBadRequest('Invalid request method');
        }

        $data = $this->request->getJSON();

        if (!isset($data->member_id)) {
            return $this->respondValidationError(['member_id' => 'Member ID is required']);
        }

        // Members can only add kin to their own profile
        if ($this->currentUser['role'] === 'member') {
            $ownMember = $this->memberModel->getMemberByUserId($this->currentUser['userId']);
            if (!$ownMember || (int) $ownMember['id'] !== (int) $data->member_id) {
                return $this->respond(['error' => 'Forbidden'], 403);
            }
        }

        $validationError = $this->validateKinAgeRule($data);
        if ($validationError !== null) {
            return $this->respondBadRequest($validationError);
        }

        $beneficiaryData = [
            'member_id'               => $data->member_id,
            'full_name'               => $data->full_name ?? '',
            'gender'                  => $data->gender ?? null,
            'relationship'            => $data->relationship ?? '',
            'benefits'                => $data->benefits ?? 0,
            'installment_one_amount'  => $data->installment_one_amount ?? 3000000,
            'installment_one_selected'=> 0,
            'installment_two_amount'  => $data->installment_two_amount ?? 3000000,
            'installment_two_selected'=> 0,
            'phone'                   => $data->phone ?? null,
            'email'                   => $data->email ?? null,
            'address'                 => $data->address ?? null,
            'date_of_birth'           => $data->date_of_birth ?? null,
        ];

        if ($this->memberKinModel->insert($beneficiaryData)) {
            $id = $this->memberKinModel->insertID();
            $this->logAudit('create', 'beneficiary', $id, null, $beneficiaryData);
            return $this->respondCreated(['beneficiaryId' => $id, 'message' => 'Next of kin added successfully']);
        }

        return $this->respondBadRequest('Failed to add next of kin: ' . implode(', ', $this->memberKinModel->errors()));
    }

    public function show($id)
    {
        if (!$this->isAuthenticated()) {
            return $this->respond(['error' => 'Unauthorized'], 401);
        }

        $beneficiary = $this->memberKinModel->find($id);

        if (!$beneficiary) {
            return $this->respondNotFound();
        }

        return $this->respond($beneficiary);
    }

    public function update($id)
    {
        if (!$this->hasAnyRole(['admin', 'member'])) {
            return $this->respond(['error' => 'Forbidden'], 403);
        }

        if (!$this->request->is('put')) {
            return $this->respondBadRequest('Invalid request method');
        }

        $beneficiary = $this->memberKinModel->find($id);
        if (!$beneficiary) {
            return $this->respondNotFound();
        }

        $data = $this->request->getJSON();
        $validationError = $this->validateKinAgeRule($data);
        if ($validationError !== null) {
            return $this->respondBadRequest($validationError);
        }

        $updateData = [];
        if (isset($data->full_name)) $updateData['full_name'] = $data->full_name;
        if (isset($data->gender)) $updateData['gender'] = $data->gender;
        if (isset($data->relationship)) $updateData['relationship'] = $data->relationship;
        if (isset($data->benefits)) $updateData['benefits'] = $data->benefits;
        if (isset($data->installment_one_amount)) $updateData['installment_one_amount'] = $data->installment_one_amount;
        if (isset($data->installment_one_selected)) $updateData['installment_one_selected'] = $data->installment_one_selected ? 1 : 0;
        if (isset($data->installment_two_amount)) $updateData['installment_two_amount'] = $data->installment_two_amount;
        if (isset($data->installment_two_selected)) $updateData['installment_two_selected'] = $data->installment_two_selected ? 1 : 0;
        if (isset($data->phone)) $updateData['phone'] = $data->phone;
        if (isset($data->email)) $updateData['email'] = $data->email;
        if (isset($data->address)) $updateData['address'] = $data->address;
        if (isset($data->date_of_birth)) $updateData['date_of_birth'] = $data->date_of_birth;

        if (isset($data->installment_one_selected) && $data->installment_one_selected) {
            $this->setSelectedInstallment((int) $beneficiary['member_id'], (int) $id, 1);
            if (!isset($updateData['installment_one_amount'])) {
                $updateData['installment_one_amount'] = 3000000;
            }
        }

        if (isset($data->installment_two_selected) && $data->installment_two_selected) {
            $this->setSelectedInstallment((int) $beneficiary['member_id'], (int) $id, 2);
            if (!isset($updateData['installment_two_amount'])) {
                $updateData['installment_two_amount'] = 3000000;
            }
        }

        if ($this->memberKinModel->update($id, $updateData)) {
            $this->logAudit('update', 'beneficiary', $id, $beneficiary, $updateData);
            return $this->respondUpdated(['message' => 'Beneficiary updated successfully']);
        }

        return $this->respondBadRequest('Failed to update beneficiary');
    }

    public function publicUpdate($id)
    {
        if (!$this->request->is('put')) {
            return $this->respondBadRequest('Invalid request method');
        }

        $data = $this->request->getJSON();
        if (!isset($data->token)) {
            return $this->respondValidationError(['token' => 'Public update token is required']);
        }

        $member = $this->getPublicMemberByToken($data->token);
        if (!$member) {
            return $this->respond(['error' => 'Public update link is invalid or expired.'], 404);
        }

        $beneficiary = $this->memberKinModel->find($id);
        if (!$beneficiary || $beneficiary['member_id'] !== $member['id']) {
            return $this->respondNotFound();
        }

        $validationError = $this->validateKinAgeRule($data);
        if ($validationError !== null) {
            return $this->respondBadRequest($validationError);
        }

        $updateData = [];
        if (isset($data->full_name)) $updateData['full_name'] = $data->full_name;
        if (isset($data->gender)) $updateData['gender'] = $data->gender;
        if (isset($data->relationship)) $updateData['relationship'] = $data->relationship;
        if (isset($data->benefits)) $updateData['benefits'] = $data->benefits;
        if (isset($data->installment_one_amount)) $updateData['installment_one_amount'] = $data->installment_one_amount;
        if (isset($data->installment_one_selected)) $updateData['installment_one_selected'] = $data->installment_one_selected ? 1 : 0;
        if (isset($data->installment_two_amount)) $updateData['installment_two_amount'] = $data->installment_two_amount;
        if (isset($data->installment_two_selected)) $updateData['installment_two_selected'] = $data->installment_two_selected ? 1 : 0;
        if (isset($data->phone)) $updateData['phone'] = $data->phone;
        if (isset($data->email)) $updateData['email'] = $data->email;
        if (isset($data->address)) $updateData['address'] = $data->address;
        if (isset($data->date_of_birth)) $updateData['date_of_birth'] = $data->date_of_birth;

        if (isset($data->installment_one_selected) && $data->installment_one_selected) {
            $this->setSelectedInstallment((int) $beneficiary['member_id'], (int) $id, 1);
            if (!isset($updateData['installment_one_amount'])) {
                $updateData['installment_one_amount'] = 3000000;
            }
        }

        if (isset($data->installment_two_selected) && $data->installment_two_selected) {
            $this->setSelectedInstallment((int) $beneficiary['member_id'], (int) $id, 2);
            if (!isset($updateData['installment_two_amount'])) {
                $updateData['installment_two_amount'] = 3000000;
            }
        }

        if ($this->memberKinModel->update($id, $updateData)) {
            $this->logAudit('update', 'beneficiary', $id, $beneficiary, $updateData);
            return $this->respondUpdated(['message' => 'Beneficiary updated successfully']);
        }

        return $this->respondBadRequest('Failed to update beneficiary');
    }

    public function delete($id)
    {
        if (!$this->isAuthenticated()) {
            return $this->respond(['error' => 'Unauthorized'], 401);
        }

        $beneficiary = $this->memberKinModel->find($id);
        if (!$beneficiary) {
            return $this->respondNotFound();
        }

        // Members can only delete their own next of kin
        if ($this->currentUser['role'] === 'member') {
            $ownMember = $this->memberModel->getMemberByUserId($this->currentUser['userId']);
            if (!$ownMember || (int) $ownMember['id'] !== (int) $beneficiary['member_id']) {
                return $this->respond(['error' => 'Forbidden'], 403);
            }
        } elseif (!$this->hasAnyRole(['admin', 'finance'])) {
            return $this->respond(['error' => 'Forbidden'], 403);
        }

        if ($this->memberKinModel->delete($id)) {
            $this->logAudit('delete', 'beneficiary', $id, $beneficiary, null);
            return $this->respond(['message' => 'Next of kin removed successfully']);
        }

        return $this->respondBadRequest('Failed to delete next of kin');
    }

    public function publicValidate()
    {
        if (!$this->request->is('post')) {
            return $this->respondBadRequest('Invalid request method');
        }

        $data = $this->request->getJSON();
        if (!isset($data->token)) {
            return $this->respondValidationError(['token' => 'Public update token is required']);
        }

        $member = $this->getPublicMemberByToken($data->token);
        if (!$member) {
            return $this->respond(['error' => 'Public update link is invalid or expired.'], 404);
        }

        $totalPercentage = $this->memberKinModel->getTotalBenefitsByMember($member['id']);
        $isValid = $totalPercentage == 100;

        return $this->respond([
            'valid' => $isValid,
            'total_percentage' => $totalPercentage,
            'message' => $isValid ? 'Distribution is valid (100%)' : "Distribution invalid (Total: {$totalPercentage}%)"
        ]);
    }

    private function setSelectedInstallment(int $memberId, int $selectedKinId, int $installmentNumber): void
    {
        $selectionColumn = $installmentNumber === 2 ? 'installment_two_selected' : 'installment_one_selected';

        $this->db->table('member_kin')
            ->where('member_id', $memberId)
            ->update([$selectionColumn => 0]);

        $this->db->table('member_kin')
            ->where('id', $selectedKinId)
            ->update([$selectionColumn => 1]);
    }

    private function validateKinAgeRule(object $data): ?string
    {
        if (!isset($data->relationship)) {
            return null;
        }

        $relationship = strtolower(trim((string) $data->relationship));
        if (!in_array($relationship, $this->adultChildRelationships, true)) {
            return null;
        }

        if (empty($data->date_of_birth)) {
            return 'Date of birth is required when relationship is Son or Daughter.';
        }

        try {
            $dob = new \DateTime((string) $data->date_of_birth);
            $today = new \DateTime('today');
            $age = $dob->diff($today)->y;

            if ($dob > $today) {
                return 'Date of birth cannot be in the future.';
            }

            if ($age < 18) {
                return 'When relationship is Son or Daughter, age must be 18 years or above.';
            }
        } catch (\Throwable $e) {
            return 'Invalid date of birth.';
        }

        return null;
    }

    public function validateDistribution($memberId)
    {
        if (!$this->isAuthenticated()) {
            return $this->respond(['error' => 'Unauthorized'], 401);
        }

        $member = $this->memberModel->find($memberId);
        if (!$member) {
            return $this->respondNotFound();
        }

        $totalPercentage = $this->memberKinModel->getTotalBenefitsByMember($memberId);
        $isValid = $totalPercentage == 100;

        return $this->respond([
            'valid' => $isValid,
            'total_percentage' => $totalPercentage,
            'message' => $isValid ? 'Distribution is valid (100%)' : "Distribution invalid (Total: {$totalPercentage}%)"
        ]);
    }
}
