<?php

declare(strict_types=1);

namespace OCA\Mitgliederverwaltung\Controller;

use OCA\Mitgliederverwaltung\Service\DuplicateMemberException;
use OCA\Mitgliederverwaltung\Service\MemberService;
use OCA\Mitgliederverwaltung\Service\PermissionService;
use OCA\Mitgliederverwaltung\Service\ValidationException;
use OCP\AppFramework\ApiController;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
use OCP\IUserSession;
use Psr\Log\LoggerInterface;

/**
 * REST API controller for member CRUD operations.
 *
 * Part of Issues #22, #61.
 */
class MemberController extends ApiController {

    use ApiControllerTrait;

    private MemberService $memberService;
    private PermissionService $permissionService;
    private IUserSession $userSession;
    private LoggerInterface $logger;

    public function __construct(
        string $appName,
        IRequest $request,
        MemberService $memberService,
        PermissionService $permissionService,
        IUserSession $userSession,
        LoggerInterface $logger
    ) {
        parent::__construct($appName, $request);
        $this->memberService = $memberService;
        $this->permissionService = $permissionService;
        $this->userSession = $userSession;
        $this->logger = $logger;
    }

    // ── Global search ───────────────────────────────────────────────

    /**
     * Full-text search across members, addresses, and notes.
     *
     * GET /api/v1/members/search?q=Mueller&limit=20
     *
     * Part of Issue #33.
     */
    #[NoCSRFRequired]
    public function search(): JSONResponse {
        try {
            $query = $this->request->getParam('q', '');
            $limit = (int)$this->request->getParam('limit', '20');

            if (trim($query) === '') {
                return new JSONResponse(['data' => []]);
            }

            $results = $this->memberService->fullTextSearch($query, $limit);

            return new JSONResponse(['data' => $results]);
        } catch (\Exception $e) {
            $this->logger->error('Failed to search members', [
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    // ── Member CRUD ──────────────────────────────────────────────────

    /**
     * List members with optional pagination and filters.
     *
     * GET /api/v1/members?limit=20&offset=0&status=aktiv&search=Mueller&filter=birthday_this_month
     *
     * Supported filter values (Part of Issue #34):
     * - birthday_this_month: members with birthday in current month
     * - unpaid_fees: members with unpaid fee records for current year
     */
    #[NoCSRFRequired]
    public function index(): JSONResponse {
        try {
            $limit = $this->request->getParam('limit');
            $offset = $this->request->getParam('offset');
            $status = $this->request->getParam('status');
            $search = $this->request->getParam('search');
            $familyId = $this->request->getParam('familyId');
            $stufeId = $this->request->getParam('stufeId');
            $rolle = $this->request->getParam('rolle');
            $birthdayThisMonth = $this->request->getParam('birthday_this_month');
            $unpaidFees = $this->request->getParam('unpaid_fees');
            // Legacy single-filter param (still supported)
            $filter = $this->request->getParam('filter');

            $limit = $limit !== null ? (int)$limit : null;
            $offset = $offset !== null ? (int)$offset : null;

            $paginated = false;

            if ($search !== null && $search !== '') {
                $members = $this->memberService->search($search);
            } elseif ($familyId !== null) {
                $members = $this->memberService->findByFamily((int)$familyId);
            } else {
                // Determine if any combinable filters are active
                $hasStatus = $status !== null && $status !== '';
                $hasRolle = $rolle !== null && $rolle !== '';
                $hasBirthday = $birthdayThisMonth === '1' || $filter === 'birthday_this_month';
                $hasUnpaid = $unpaidFees === '1' || $filter === 'unpaid_fees';

                if ($hasStatus || $hasRolle || $hasBirthday || $hasUnpaid) {
                    $members = $this->memberService->findFiltered(
                        $hasStatus ? $status : null,
                        $hasRolle ? $rolle : null,
                        $hasBirthday,
                        $hasUnpaid
                    );
                } else {
                    $members = $this->memberService->findAll($limit, $offset);
                    $paginated = $limit !== null;
                }
            }

            // When paginated, count($members) is just the page size — query the true total.
            $total = $paginated ? $this->memberService->countAll() : count($members);

            return new JSONResponse([
                'data' => $members,
                'total' => $total,
            ]);
        } catch (\Exception $e) {
            $this->logger->error('Failed to list members', [
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Get a single member with all sub-entities.
     *
     * GET /api/v1/members/{id}
     */
    #[NoCSRFRequired]
    public function show(int $id): JSONResponse {
        try {
            $member = $this->memberService->find($id);
            return new JSONResponse($member);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(
                ['error' => 'Mitglied nicht gefunden'],
                Http::STATUS_NOT_FOUND
            );
        } catch (\Exception $e) {
            $this->logger->error('Failed to get member', [
                'memberId' => $id,
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Create a new member.
     *
     * POST /api/v1/members
     */
    public function create(): JSONResponse {
        try {
            $data = $this->getRequestData();
            $addresses = $data['addresses'] ?? [];
            $phones = $data['phones'] ?? [];
            $emails = $data['emails'] ?? [];

            unset($data['addresses'], $data['phones'], $data['emails']);

            $member = $this->memberService->create($data, $addresses, $phones, $emails);

            return new JSONResponse($member, Http::STATUS_CREATED);
        } catch (ValidationException $e) {
            return new JSONResponse(
                ['error' => $e->getMessage()],
                Http::STATUS_BAD_REQUEST
            );
        } catch (DuplicateMemberException $e) {
            return new JSONResponse(
                ['error' => $e->getMessage()],
                Http::STATUS_CONFLICT
            );
        } catch (\Exception $e) {
            $this->logger->error('Failed to create member', [
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Update an existing member.
     *
     * PUT /api/v1/members/{id}
     */
    public function update(int $id): JSONResponse {
        try {
            $data = $this->getRequestData();
            $member = $this->memberService->update($id, $data);

            return new JSONResponse($member);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(
                ['error' => 'Mitglied nicht gefunden'],
                Http::STATUS_NOT_FOUND
            );
        } catch (ValidationException $e) {
            return new JSONResponse(
                ['error' => $e->getMessage()],
                Http::STATUS_BAD_REQUEST
            );
        } catch (\Exception $e) {
            $this->logger->error('Failed to update member', [
                'memberId' => $id,
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Soft-delete a member.
     *
     * DELETE /api/v1/members/{id}
     */
    public function destroy(int $id): JSONResponse {
        try {
            $this->memberService->delete($id);
            return new JSONResponse(['status' => 'deleted']);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(
                ['error' => 'Mitglied nicht gefunden'],
                Http::STATUS_NOT_FOUND
            );
        } catch (\Exception $e) {
            $this->logger->error('Failed to delete member', [
                'memberId' => $id,
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    // ── Admin-only: bulk-reveal encrypted Allergien ─────────────────

    /**
     * Reveal decrypted Allergien for every non-deleted member.
     *
     * POST /api/v1/members/allergien/reveal
     *
     * Admin-only (enforced by AuthorizationMiddleware).
     * Each reveal is audited. Response is not cached; clients are expected
     * to drop the plaintext from memory after use.
     *
     * @return JSONResponse { data: { [memberId]: plaintext|null } }
     */
    public function revealAllergies(): JSONResponse {
        try {
            $user = $this->userSession->getUser();
            $userId = $user !== null ? $user->getUID() : 'unknown';

            $map = $this->memberService->revealAllAllergies($userId);

            $response = new JSONResponse(['data' => $map]);
            $response->addHeader('Cache-Control', 'no-store');
            return $response;
        } catch (\Exception $e) {
            $this->logger->error('Allergien reveal fehlgeschlagen', [
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Allergien konnten nicht entschluesselt werden.'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    // ── Address sub-entity endpoints ─────────────────────────────────

    /**
     * Add an address to a member.
     *
     * POST /api/v1/members/{memberId}/addresses
     */
    public function createAddress(int $memberId): JSONResponse {
        try {
            // Verify member exists
            $this->memberService->find($memberId);

            $data = $this->getRequestData();
            $address = $this->memberService->addAddress($memberId, $data);

            return new JSONResponse($address, Http::STATUS_CREATED);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(
                ['error' => 'Mitglied nicht gefunden'],
                Http::STATUS_NOT_FOUND
            );
        } catch (\Exception $e) {
            $this->logger->error('Failed to create address', [
                'memberId' => $memberId,
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Update an address.
     *
     * PUT /api/v1/members/{memberId}/addresses/{addrId}
     */
    public function updateAddress(int $memberId, int $addrId): JSONResponse {
        try {
            $data = $this->getRequestData();
            $address = $this->memberService->updateAddress($addrId, $data);

            return new JSONResponse($address);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(
                ['error' => 'Adresse nicht gefunden'],
                Http::STATUS_NOT_FOUND
            );
        } catch (\Exception $e) {
            $this->logger->error('Failed to update address', [
                'memberId' => $memberId,
                'addrId' => $addrId,
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Delete an address.
     *
     * DELETE /api/v1/members/{memberId}/addresses/{addrId}
     */
    public function destroyAddress(int $memberId, int $addrId): JSONResponse {
        try {
            $this->memberService->deleteAddress($addrId);
            return new JSONResponse(['status' => 'deleted']);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(
                ['error' => 'Adresse nicht gefunden'],
                Http::STATUS_NOT_FOUND
            );
        } catch (\Exception $e) {
            $this->logger->error('Failed to delete address', [
                'memberId' => $memberId,
                'addrId' => $addrId,
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    // ── Phone sub-entity endpoints ───────────────────────────────────

    /**
     * Add a phone number to a member.
     *
     * POST /api/v1/members/{memberId}/phones
     */
    public function createPhone(int $memberId): JSONResponse {
        try {
            $this->memberService->find($memberId);

            $data = $this->getRequestData();
            $phone = $this->memberService->addPhone($memberId, $data);

            return new JSONResponse($phone, Http::STATUS_CREATED);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(
                ['error' => 'Mitglied nicht gefunden'],
                Http::STATUS_NOT_FOUND
            );
        } catch (\Exception $e) {
            $this->logger->error('Failed to create phone', [
                'memberId' => $memberId,
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Update a phone number.
     *
     * PUT /api/v1/members/{memberId}/phones/{phoneId}
     */
    public function updatePhone(int $memberId, int $phoneId): JSONResponse {
        try {
            $data = $this->getRequestData();
            $phone = $this->memberService->updatePhone($phoneId, $data);

            return new JSONResponse($phone);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(
                ['error' => 'Telefonnummer nicht gefunden'],
                Http::STATUS_NOT_FOUND
            );
        } catch (\Exception $e) {
            $this->logger->error('Failed to update phone', [
                'memberId' => $memberId,
                'phoneId' => $phoneId,
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Delete a phone number.
     *
     * DELETE /api/v1/members/{memberId}/phones/{phoneId}
     */
    public function destroyPhone(int $memberId, int $phoneId): JSONResponse {
        try {
            $this->memberService->deletePhone($phoneId);
            return new JSONResponse(['status' => 'deleted']);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(
                ['error' => 'Telefonnummer nicht gefunden'],
                Http::STATUS_NOT_FOUND
            );
        } catch (\Exception $e) {
            $this->logger->error('Failed to delete phone', [
                'memberId' => $memberId,
                'phoneId' => $phoneId,
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    // ── Email sub-entity endpoints ───────────────────────────────────

    /**
     * Add an email address to a member.
     *
     * POST /api/v1/members/{memberId}/emails
     */
    public function createEmail(int $memberId): JSONResponse {
        try {
            $this->memberService->find($memberId);

            $data = $this->getRequestData();
            $email = $this->memberService->addEmail($memberId, $data);

            return new JSONResponse($email, Http::STATUS_CREATED);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(
                ['error' => 'Mitglied nicht gefunden'],
                Http::STATUS_NOT_FOUND
            );
        } catch (\Exception $e) {
            $this->logger->error('Failed to create email', [
                'memberId' => $memberId,
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Update an email address.
     *
     * PUT /api/v1/members/{memberId}/emails/{emailId}
     */
    public function updateEmail(int $memberId, int $emailId): JSONResponse {
        try {
            $data = $this->getRequestData();
            $email = $this->memberService->updateEmail($emailId, $data);

            return new JSONResponse($email);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(
                ['error' => 'E-Mail-Adresse nicht gefunden'],
                Http::STATUS_NOT_FOUND
            );
        } catch (\Exception $e) {
            $this->logger->error('Failed to update email', [
                'memberId' => $memberId,
                'emailId' => $emailId,
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Delete an email address.
     *
     * DELETE /api/v1/members/{memberId}/emails/{emailId}
     */
    public function destroyEmail(int $memberId, int $emailId): JSONResponse {
        try {
            $this->memberService->deleteEmail($emailId);
            return new JSONResponse(['status' => 'deleted']);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(
                ['error' => 'E-Mail-Adresse nicht gefunden'],
                Http::STATUS_NOT_FOUND
            );
        } catch (\Exception $e) {
            $this->logger->error('Failed to delete email', [
                'memberId' => $memberId,
                'emailId' => $emailId,
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    // ── Archive (soft-deleted members) ───────────────────────────────

    /**
     * List archived (soft-deleted) members.
     * Admin-only access.
     *
     * GET /api/v1/members/archive
     *
     * Part of Issue #61.
     */
    #[NoCSRFRequired]
    public function archive(): JSONResponse {
        try {
            $userId = $this->getCurrentUserId();
            if (!$this->permissionService->isAdmin($userId)) {
                return new JSONResponse(
                    ['error' => 'Nur Administratoren koennen das Archiv einsehen'],
                    Http::STATUS_FORBIDDEN
                );
            }

            $limit = $this->request->getParam('limit');
            $offset = $this->request->getParam('offset');
            $limit = $limit !== null ? (int)$limit : null;
            $offset = $offset !== null ? (int)$offset : null;

            $archived = $this->memberService->findArchived($limit, $offset);
            $total = $this->memberService->countArchived();

            return new JSONResponse([
                'data' => $archived,
                'total' => $total,
            ]);
        } catch (\Exception $e) {
            $this->logger->error('Failed to list archived members', [
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    // ── Helpers ──────────────────────────────────────────────────────

    private function getCurrentUserId(): string {
        $user = $this->userSession->getUser();
        return $user ? $user->getUID() : 'system';
    }

    // getRequestData() provided by ApiControllerTrait
}
