<?php

declare(strict_types=1);

namespace OCA\Mitgliederverwaltung\Controller;

use OCA\Mitgliederverwaltung\Service\InventoryService;
use OCA\Mitgliederverwaltung\Service\SaleService;
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 inventory CRUD operations.
 *
 * Part of Issue #165 (Inventory Tracking).
 */
class InventoryController extends ApiController {

    use ApiControllerTrait;

    private InventoryService $inventoryService;
    private SaleService $saleService;
    private IUserSession $userSession;
    private LoggerInterface $logger;

    public function __construct(
        string $appName,
        IRequest $request,
        InventoryService $inventoryService,
        SaleService $saleService,
        IUserSession $userSession,
        LoggerInterface $logger
    ) {
        parent::__construct($appName, $request);
        $this->inventoryService = $inventoryService;
        $this->saleService = $saleService;
        $this->userSession = $userSession;
        $this->logger = $logger;
    }

    // ── Categories ──────────────────────────────────────────────────

    /**
     * List all categories.
     *
     * GET /api/v1/inventory/categories
     */
    #[NoCSRFRequired]
    public function listCategories(): JSONResponse {
        try {
            $categories = $this->inventoryService->getCategories();
            return new JSONResponse(['data' => array_map(fn($c) => $c->jsonSerialize(), $categories)]);
        } catch (\Exception $e) {
            $this->logger->error('Failed to list inventory categories', ['exception' => $e, 'app' => 'mitgliederverwaltung']);
            return new JSONResponse(['error' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * Create a category.
     *
     * POST /api/v1/inventory/categories
     */
    public function createCategory(): JSONResponse {
        try {
            $data = $this->getRequestData();
            if (empty($data['name'])) {
                return new JSONResponse(['error' => 'Name ist erforderlich'], Http::STATUS_BAD_REQUEST);
            }
            $category = $this->inventoryService->createCategory($data);
            return new JSONResponse($category->jsonSerialize(), Http::STATUS_CREATED);
        } catch (\Exception $e) {
            $this->logger->error('Failed to create inventory category', ['exception' => $e, 'app' => 'mitgliederverwaltung']);
            return new JSONResponse(['error' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * Update a category.
     *
     * PUT /api/v1/inventory/categories/{id}
     */
    public function updateCategory(int $id): JSONResponse {
        try {
            $data = $this->getRequestData();
            $category = $this->inventoryService->updateCategory($id, $data);
            return new JSONResponse($category->jsonSerialize());
        } catch (DoesNotExistException $e) {
            return new JSONResponse(['error' => 'Kategorie nicht gefunden'], Http::STATUS_NOT_FOUND);
        } catch (\Exception $e) {
            $this->logger->error('Failed to update inventory category', ['exception' => $e, 'app' => 'mitgliederverwaltung']);
            return new JSONResponse(['error' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * Delete a category.
     *
     * DELETE /api/v1/inventory/categories/{id}
     */
    public function deleteCategory(int $id): JSONResponse {
        try {
            $this->inventoryService->deleteCategory($id);
            return new JSONResponse(['status' => 'deleted']);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(['error' => 'Kategorie nicht gefunden'], Http::STATUS_NOT_FOUND);
        } catch (\Exception $e) {
            $this->logger->error('Failed to delete inventory category', ['exception' => $e, 'app' => 'mitgliederverwaltung']);
            return new JSONResponse(['error' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
        }
    }

    // ── General Material ────────────────────────────────────────────

    /**
     * List all general material items.
     *
     * GET /api/v1/inventory/general
     */
    #[NoCSRFRequired]
    public function listGeneral(): JSONResponse {
        try {
            $items = $this->inventoryService->getGeneralMaterial();
            $result = [];
            foreach ($items as $item) {
                $data = $item->jsonSerialize();
                $data['categories'] = $this->inventoryService->getCategoryNamesForItem($item->getId());
                $result[] = $data;
            }
            return new JSONResponse(['data' => $result]);
        } catch (\Exception $e) {
            $this->logger->error('Failed to list general material', ['exception' => $e, 'app' => 'mitgliederverwaltung']);
            return new JSONResponse(['error' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * Get a single general material item.
     *
     * GET /api/v1/inventory/general/{id}
     */
    #[NoCSRFRequired]
    public function showGeneral(int $id): JSONResponse {
        try {
            $item = $this->inventoryService->getGeneralMaterialById($id);
            $data = $item->jsonSerialize();
            $data['categories'] = $this->inventoryService->getCategoryNamesForItem($id);
            return new JSONResponse($data);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(['error' => 'Material nicht gefunden'], Http::STATUS_NOT_FOUND);
        } catch (\Exception $e) {
            $this->logger->error('Failed to get general material', ['exception' => $e, 'app' => 'mitgliederverwaltung']);
            return new JSONResponse(['error' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * Create a general material item.
     *
     * POST /api/v1/inventory/general
     */
    public function createGeneral(): JSONResponse {
        try {
            $data = $this->getRequestData();
            if (empty($data['name'])) {
                return new JSONResponse(['error' => 'Name ist erforderlich'], Http::STATUS_BAD_REQUEST);
            }
            $categoryIds = $data['categories'] ?? null;
            unset($data['categories']);
            $item = $this->inventoryService->createGeneralMaterial($data, $categoryIds);
            $result = $item->jsonSerialize();
            $result['categories'] = $this->inventoryService->getCategoryNamesForItem($item->getId());
            return new JSONResponse($result, Http::STATUS_CREATED);
        } catch (\Exception $e) {
            $this->logger->error('Failed to create general material', ['exception' => $e, 'app' => 'mitgliederverwaltung']);
            return new JSONResponse(['error' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * Update a general material item.
     *
     * PUT /api/v1/inventory/general/{id}
     */
    public function updateGeneral(int $id): JSONResponse {
        try {
            $data = $this->getRequestData();
            $categoryIds = $data['categories'] ?? null;
            unset($data['categories']);
            $item = $this->inventoryService->updateGeneralMaterial($id, $data, $categoryIds);
            $result = $item->jsonSerialize();
            $result['categories'] = $this->inventoryService->getCategoryNamesForItem($id);
            return new JSONResponse($result);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(['error' => 'Material nicht gefunden'], Http::STATUS_NOT_FOUND);
        } catch (\Exception $e) {
            $this->logger->error('Failed to update general material', ['exception' => $e, 'app' => 'mitgliederverwaltung']);
            return new JSONResponse(['error' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * Delete a general material item.
     *
     * DELETE /api/v1/inventory/general/{id}
     */
    public function deleteGeneral(int $id): JSONResponse {
        try {
            $this->inventoryService->deleteGeneralMaterial($id);
            return new JSONResponse(['status' => 'deleted']);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(['error' => 'Material nicht gefunden'], Http::STATUS_NOT_FOUND);
        } catch (\Exception $e) {
            $this->logger->error('Failed to delete general material', ['exception' => $e, 'app' => 'mitgliederverwaltung']);
            return new JSONResponse(['error' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
        }
    }

    // ── Stock Items ─────────────────────────────────────────────────

    /**
     * List all stock items with their variants.
     *
     * GET /api/v1/inventory/stock
     */
    #[NoCSRFRequired]
    public function listStock(): JSONResponse {
        try {
            $items = $this->inventoryService->getStockItems();
            $result = [];
            foreach ($items as $item) {
                $data = $item->jsonSerialize();
                $data['categories'] = $this->inventoryService->getCategoryNamesForItem($item->getId());
                $data['variants'] = array_map(
                    fn($v) => $v->jsonSerialize(),
                    $this->inventoryService->getVariantsByStockItem($item->getId())
                );

                // Calculate total amount from variants
                $totalAmount = 0;
                foreach ($data['variants'] as $v) {
                    $totalAmount += (int)($v['amount'] ?? 0);
                }
                $data['total_amount'] = $totalAmount;

                // Calculate total minimum threshold
                $minThreshold = 0;
                foreach ($data['variants'] as $v) {
                    $minThreshold += (int)($v['min_threshold'] ?? 0);
                }
                $data['min_threshold'] = $minThreshold;

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

    /**
     * Get a single stock item with its variants.
     *
     * GET /api/v1/inventory/stock/{id}
     */
    #[NoCSRFRequired]
    public function showStock(int $id): JSONResponse {
        try {
            $item = $this->inventoryService->getStockItemById($id);
            $data = $item->jsonSerialize();
            $data['categories'] = $this->inventoryService->getCategoryNamesForItem($id);
            $data['variants'] = array_map(
                fn($v) => $v->jsonSerialize(),
                $this->inventoryService->getVariantsByStockItem($id)
            );
            return new JSONResponse($data);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(['error' => 'Verkaufsmaterial nicht gefunden'], Http::STATUS_NOT_FOUND);
        } catch (\Exception $e) {
            $this->logger->error('Failed to get stock item', ['exception' => $e, 'app' => 'mitgliederverwaltung']);
            return new JSONResponse(['error' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * Create a stock item.
     *
     * POST /api/v1/inventory/stock
     */
    public function createStock(): JSONResponse {
        try {
            $data = $this->getRequestData();
            if (empty($data['name'])) {
                return new JSONResponse(['error' => 'Name ist erforderlich'], Http::STATUS_BAD_REQUEST);
            }
            $categoryIds = $data['categories'] ?? null;
            $variants = $data['variants'] ?? [];
            unset($data['categories'], $data['variants']);

            $item = $this->inventoryService->createStockItem($data, $categoryIds);

            // Create variants if provided
            foreach ($variants as $vData) {
                $vData['stock_item_id'] = $item->getId();
                $this->inventoryService->createStockVariant($vData);
            }

            $result = $item->jsonSerialize();
            $result['categories'] = $this->inventoryService->getCategoryNamesForItem($item->getId());
            return new JSONResponse($result, Http::STATUS_CREATED);
        } catch (\Exception $e) {
            $this->logger->error('Failed to create stock item', ['exception' => $e, 'app' => 'mitgliederverwaltung']);
            return new JSONResponse(['error' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * Update a stock item.
     *
     * PUT /api/v1/inventory/stock/{id}
     */
    public function updateStock(int $id): JSONResponse {
        try {
            $data = $this->getRequestData();
            $categoryIds = $data['categories'] ?? null;
            $variants = $data['variants'] ?? [];
            unset($data['categories'], $data['variants']);

            $item = $this->inventoryService->updateStockItem($id, $data, $categoryIds);

            // Update/create/delete variants
            foreach ($variants as $vData) {
                if (isset($vData['id']) && is_numeric($vData['id'])) {
                    // Update existing variant
                    $this->inventoryService->updateStockVariant((int)$vData['id'], $vData);
                } else {
                    // Create new variant
                    $vData['stock_item_id'] = $id;
                    $this->inventoryService->createStockVariant($vData);
                }
            }

            // Delete variants that are no longer present
            $existing = $this->inventoryService->getVariantsByStockItem($id);
            $presentIds = array_map(fn($v) => (int)($v['id'] ?? 0), $variants);
            foreach ($existing as $existingVariant) {
                if (!in_array((int)$existingVariant->getId(), $presentIds, true)) {
                    $this->inventoryService->deleteStockVariant((int)$existingVariant->getId());
                }
            }

            $result = $item->jsonSerialize();
            $result['categories'] = $this->inventoryService->getCategoryNamesForItem($id);
            return new JSONResponse($result);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(['error' => 'Verkaufsmaterial nicht gefunden'], Http::STATUS_NOT_FOUND);
        } catch (\Exception $e) {
            $this->logger->error('Failed to update stock item', ['exception' => $e, 'app' => 'mitgliederverwaltung']);
            return new JSONResponse(['error' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * Delete a stock item.
     *
     * DELETE /api/v1/inventory/stock/{id}
     */
    public function deleteStock(int $id): JSONResponse {
        try {
            $this->inventoryService->deleteStockItem($id);
            return new JSONResponse(['status' => 'deleted']);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(['error' => 'Verkaufsmaterial nicht gefunden'], Http::STATUS_NOT_FOUND);
        } catch (\Exception $e) {
            $this->logger->error('Failed to delete stock item', ['exception' => $e, 'app' => 'mitgliederverwaltung']);
            return new JSONResponse(['error' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
        }
    }

    // ── Variants ────────────────────────────────────────────────────

    /**
     * Create a variant (standalone endpoint).
     *
     * POST /api/v1/inventory/variants
     */
    public function createVariant(): JSONResponse {
        try {
            $data = $this->getRequestData();
            $variant = $this->inventoryService->createStockVariant($data);
            return new JSONResponse($variant->jsonSerialize(), Http::STATUS_CREATED);
        } catch (\Exception $e) {
            $this->logger->error('Failed to create stock variant', ['exception' => $e, 'app' => 'mitgliederverwaltung']);
            return new JSONResponse(['error' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * Update a variant.
     *
     * PUT /api/v1/inventory/variants/{id}
     */
    public function updateVariant(int $id): JSONResponse {
        try {
            $data = $this->getRequestData();
            $variant = $this->inventoryService->updateStockVariant($id, $data);
            return new JSONResponse($variant->jsonSerialize());
        } catch (DoesNotExistException $e) {
            return new JSONResponse(['error' => 'Variante nicht gefunden'], Http::STATUS_NOT_FOUND);
        } catch (\Exception $e) {
            $this->logger->error('Failed to update stock variant', ['exception' => $e, 'app' => 'mitgliederverwaltung']);
            return new JSONResponse(['error' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * Delete a variant.
     *
     * DELETE /api/v1/inventory/variants/{id}
     */
    public function deleteVariant(int $id): JSONResponse {
        try {
            $this->inventoryService->deleteStockVariant($id);
            return new JSONResponse(['status' => 'deleted']);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(['error' => 'Variante nicht gefunden'], Http::STATUS_NOT_FOUND);
        } catch (\Exception $e) {
            $this->logger->error('Failed to delete stock variant', ['exception' => $e, 'app' => 'mitgliederverwaltung']);
            return new JSONResponse(['error' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
        }
    }

    // ── Sales ───────────────────────────────────────────────────────

    /**
     * List all sales.
     *
     * GET /api/v1/inventory/sales
     */
    #[NoCSRFRequired]
    public function listSales(): JSONResponse {
        try {
            $from = $this->request->getParam('dateFrom');
            $to = $this->request->getParam('dateTo');
            $sales = $this->saleService->getSalesByDateRange($from, $to);
            return new JSONResponse(['data' => array_map(fn($s) => $s->jsonSerialize(), $sales)]);
        } catch (\Exception $e) {
            $this->logger->error('Failed to list sales', ['exception' => $e, 'app' => 'mitgliederverwaltung']);
            return new JSONResponse(['error' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * Get a single sale.
     *
     * GET /api/v1/inventory/sales/{id}
     */
    #[NoCSRFRequired]
    public function showSale(int $id): JSONResponse {
        try {
            $sale = $this->saleService->getSaleById($id);
            return new JSONResponse($sale->jsonSerialize());
        } catch (DoesNotExistException $e) {
            return new JSONResponse(['error' => 'Verkauf nicht gefunden'], Http::STATUS_NOT_FOUND);
        } catch (\Exception $e) {
            $this->logger->error('Failed to get sale', ['exception' => $e, 'app' => 'mitgliederverwaltung']);
            return new JSONResponse(['error' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * Create a sale record.
     *
     * POST /api/v1/inventory/sales
     */
    public function createSale(): JSONResponse {
        try {
            $data = $this->getRequestData();
            $sale = $this->saleService->createSale($data);
            return new JSONResponse($sale->jsonSerialize(), Http::STATUS_CREATED);
        } catch (\Exception $e) {
            $this->logger->error('Failed to create sale', ['exception' => $e, 'app' => 'mitgliederverwaltung']);
            return new JSONResponse(['error' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * Delete a sale record.
     *
     * DELETE /api/v1/inventory/sales/{id}
     */
    public function deleteSale(int $id): JSONResponse {
        try {
            $this->saleService->deleteSale($id);
            return new JSONResponse(['status' => 'deleted']);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(['error' => 'Verkauf nicht gefunden'], Http::STATUS_NOT_FOUND);
        } catch (\Exception $e) {
            $this->logger->error('Failed to delete sale', ['exception' => $e, 'app' => 'mitgliederverwaltung']);
            return new JSONResponse(['error' => 'Internal server error'], Http::STATUS_INTERNAL_SERVER_ERROR);
        }
    }
}
