import { Response, NextFunction } from 'express';
import { BaseController } from '../../../shared/controllers/base.controller';
import { AuthenticatedRequest } from '../../../shared/types';
import { posSessionService } from '../services/pos-session.service';
import { posOrderService } from '../services/pos-order.service';

class POSController extends BaseController {
  // ==================== SESSION ENDPOINTS ====================

  /**
   * POST /pos/sessions/open - Open a new session
   */
  async openSession(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<Response | void> {
    try {
      const tenantId = this.getTenantId(req);
      const userId = this.getUserId(req);
      const branchId = this.getBranchId(req);

      if (!branchId) {
        return this.error(res, 'BRANCH_REQUIRED', 'X-Branch-ID header is required');
      }

      const { registerId, openingCash, openingNotes } = req.body;

      if (!registerId) {
        return this.validationError(res, { registerId: 'Required' });
      }

      const result = await posSessionService.openSession(tenantId, {
        branchId,
        registerId,
        userId,
        openingCash: openingCash || 0,
        openingNotes,
      });

      if (!result.success) {
        return this.error(res, result.error.code, result.error.message);
      }

      return this.success(res, result.data, 201);
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /pos/sessions/:id/close - Close a session
   */
  async closeSession(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<Response | void> {
    try {
      const tenantId = this.getTenantId(req);
      const userId = this.getUserId(req);
      const { id } = req.params;

      const { closingCashCounted, closingNotes, cashCountDetail } = req.body;

      if (closingCashCounted === undefined) {
        return this.validationError(res, { closingCashCounted: 'Required' });
      }

      const result = await posSessionService.closeSession(tenantId, id, {
        closedBy: userId,
        closingCashCounted,
        closingNotes,
        cashCountDetail,
      });

      if (!result.success) {
        return this.error(res, result.error.code, result.error.message);
      }

      return this.success(res, result.data);
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /pos/sessions/active - Get active session for current user
   */
  async getActiveSession(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<Response | void> {
    try {
      const tenantId = this.getTenantId(req);
      const userId = this.getUserId(req);

      const session = await posSessionService.getActiveSession(tenantId, userId);

      if (!session) {
        return this.notFound(res, 'Active session');
      }

      return this.success(res, session);
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /pos/sessions/:id - Get session by ID
   */
  async getSession(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<Response | void> {
    try {
      const tenantId = this.getTenantId(req);
      const { id } = req.params;
      const { withOrders } = req.query;

      const session = withOrders
        ? await posSessionService.getSessionWithOrders(tenantId, id)
        : await posSessionService.findById(tenantId, id);

      if (!session) {
        return this.notFound(res, 'Session');
      }

      return this.success(res, session);
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /pos/sessions - List sessions for branch
   */
  async listSessions(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<Response | void> {
    try {
      const tenantId = this.getTenantId(req);
      const branchId = this.getBranchId(req);

      if (!branchId) {
        return this.error(res, 'BRANCH_REQUIRED', 'X-Branch-ID header is required');
      }

      const pagination = this.parsePagination(req.query);
      const { status, userId, startDate, endDate } = req.query;

      const result = await posSessionService.findAll(tenantId, {
        pagination,
        filters: [
          { field: 'branchId', operator: 'eq', value: branchId },
          ...(status ? [{ field: 'status', operator: 'eq' as const, value: status }] : []),
          ...(userId ? [{ field: 'userId', operator: 'eq' as const, value: userId }] : []),
        ],
      });

      return this.paginated(res, result);
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /pos/sessions/daily-summary - Get daily session summary
   */
  async getDailySummary(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<Response | void> {
    try {
      const tenantId = this.getTenantId(req);
      const branchId = this.getBranchId(req);

      if (!branchId) {
        return this.error(res, 'BRANCH_REQUIRED', 'X-Branch-ID header is required');
      }

      const dateParam = req.query.date as string;
      const date = dateParam ? new Date(dateParam) : new Date();

      const summary = await posSessionService.getDailySummary(tenantId, branchId, date);
      return this.success(res, summary);
    } catch (error) {
      next(error);
    }
  }

  // ==================== ORDER ENDPOINTS ====================

  /**
   * POST /pos/orders - Create a new order
   */
  async createOrder(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<Response | void> {
    try {
      const tenantId = this.getTenantId(req);
      const userId = this.getUserId(req);
      const branchId = this.getBranchId(req);
      const branchCode = req.branch?.branchCode || 'UNK';

      if (!branchId) {
        return this.error(res, 'BRANCH_REQUIRED', 'X-Branch-ID header is required');
      }

      const { sessionId, registerId, lines, ...orderData } = req.body;

      if (!sessionId || !registerId || !lines || lines.length === 0) {
        return this.validationError(res, {
          sessionId: sessionId ? undefined : 'Required',
          registerId: registerId ? undefined : 'Required',
          lines: lines?.length ? undefined : 'At least one line is required',
        });
      }

      const result = await posOrderService.createOrder(
        tenantId,
        {
          sessionId,
          branchId,
          registerId,
          userId,
          lines,
          ...orderData,
        },
        branchCode
      );

      if (!result.success) {
        return this.error(res, result.error.code, result.error.message);
      }

      return this.success(res, result.data, 201);
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /pos/orders/:id/payments - Add payment to order
   */
  async addPayment(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<Response | void> {
    try {
      const tenantId = this.getTenantId(req);
      const userId = this.getUserId(req);
      const { id } = req.params;

      const { method, amount, amountReceived, ...paymentData } = req.body;

      if (!method || amount === undefined) {
        return this.validationError(res, {
          method: method ? undefined : 'Required',
          amount: amount !== undefined ? undefined : 'Required',
        });
      }

      const result = await posOrderService.addPayment(
        tenantId,
        id,
        {
          method,
          amount,
          amountReceived: amountReceived || amount,
          ...paymentData,
        },
        userId
      );

      if (!result.success) {
        return this.error(res, result.error.code, result.error.message);
      }

      return this.success(res, result.data);
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /pos/orders/:id/void - Void an order
   */
  async voidOrder(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<Response | void> {
    try {
      const tenantId = this.getTenantId(req);
      const userId = this.getUserId(req);
      const { id } = req.params;
      const { reason } = req.body;

      if (!reason) {
        return this.validationError(res, { reason: 'Required' });
      }

      const result = await posOrderService.voidOrder(tenantId, id, reason, userId);

      if (!result.success) {
        return this.error(res, result.error.code, result.error.message);
      }

      return this.success(res, result.data);
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /pos/orders/:id - Get order by ID
   */
  async getOrder(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<Response | void> {
    try {
      const tenantId = this.getTenantId(req);
      const { id } = req.params;

      const order = await posOrderService.getOrderWithDetails(tenantId, id);

      if (!order) {
        return this.notFound(res, 'Order');
      }

      return this.success(res, order);
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /pos/orders - List orders
   */
  async listOrders(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<Response | void> {
    try {
      const tenantId = this.getTenantId(req);
      const branchId = this.getBranchId(req);

      if (!branchId) {
        return this.error(res, 'BRANCH_REQUIRED', 'X-Branch-ID header is required');
      }

      const pagination = this.parsePagination(req.query);
      const { sessionId, status, customerId, startDate, endDate, search } = req.query;

      // If search term provided, use search method
      if (search) {
        const orders = await posOrderService.searchOrders(
          tenantId,
          branchId,
          search as string,
          pagination.limit
        );
        return this.success(res, orders);
      }

      const result = await posOrderService.findAll(tenantId, {
        pagination,
        filters: [
          { field: 'branchId', operator: 'eq', value: branchId },
          ...(sessionId ? [{ field: 'sessionId', operator: 'eq' as const, value: sessionId }] : []),
          ...(status ? [{ field: 'status', operator: 'eq' as const, value: status }] : []),
          ...(customerId ? [{ field: 'customerId', operator: 'eq' as const, value: customerId }] : []),
        ],
      });

      return this.paginated(res, result);
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /pos/orders/session/:sessionId - Get orders by session
   */
  async getOrdersBySession(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<Response | void> {
    try {
      const tenantId = this.getTenantId(req);
      const { sessionId } = req.params;

      const orders = await posOrderService.getOrdersBySession(tenantId, sessionId);
      return this.success(res, orders);
    } catch (error) {
      next(error);
    }
  }

  // ==================== REFUND ENDPOINTS ====================

  /**
   * POST /pos/refunds - Create a refund
   */
  async createRefund(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<Response | void> {
    try {
      const tenantId = this.getTenantId(req);
      const userId = this.getUserId(req);
      const branchId = this.getBranchId(req);
      const branchCode = req.branch?.branchCode || 'UNK';

      if (!branchId) {
        return this.error(res, 'BRANCH_REQUIRED', 'X-Branch-ID header is required');
      }

      const { originalOrderId, sessionId, registerId, lines, refundReason } = req.body;

      if (!originalOrderId || !sessionId || !registerId || !lines || lines.length === 0) {
        return this.validationError(res, {
          originalOrderId: originalOrderId ? undefined : 'Required',
          sessionId: sessionId ? undefined : 'Required',
          registerId: registerId ? undefined : 'Required',
          lines: lines?.length ? undefined : 'At least one line is required',
        });
      }

      const result = await posOrderService.createRefund(
        tenantId,
        {
          originalOrderId,
          sessionId,
          branchId,
          registerId,
          userId,
          lines,
          refundReason: refundReason || 'Customer refund',
        },
        branchCode
      );

      if (!result.success) {
        return this.error(res, result.error.code, result.error.message);
      }

      return this.success(res, result.data, 201);
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /pos/refunds/:id/process - Process refund payment
   */
  async processRefund(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<Response | void> {
    try {
      const tenantId = this.getTenantId(req);
      const userId = this.getUserId(req);
      const { id } = req.params;
      const { method, amount } = req.body;

      if (!method || amount === undefined) {
        return this.validationError(res, {
          method: method ? undefined : 'Required',
          amount: amount !== undefined ? undefined : 'Required',
        });
      }

      const result = await posOrderService.processRefundPayment(tenantId, id, { method, amount }, userId);

      if (!result.success) {
        return this.error(res, result.error.code, result.error.message);
      }

      return this.success(res, result.data);
    } catch (error) {
      next(error);
    }
  }

  // ==================== HOLD/RECALL ENDPOINTS ====================

  /**
   * POST /pos/orders/:id/hold - Hold an order
   */
  async holdOrder(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<Response | void> {
    try {
      const tenantId = this.getTenantId(req);
      const { id } = req.params;
      const { holdName } = req.body;

      const result = await posOrderService.holdOrder(tenantId, id, holdName || 'Unnamed');

      if (!result.success) {
        return this.error(res, result.error.code, result.error.message);
      }

      return this.success(res, result.data);
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /pos/orders/held - Get held orders
   */
  async getHeldOrders(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<Response | void> {
    try {
      const tenantId = this.getTenantId(req);
      const branchId = this.getBranchId(req);

      if (!branchId) {
        return this.error(res, 'BRANCH_REQUIRED', 'X-Branch-ID header is required');
      }

      const orders = await posOrderService.getHeldOrders(tenantId, branchId);
      return this.success(res, orders);
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /pos/orders/:id/recall - Recall a held order
   */
  async recallOrder(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<Response | void> {
    try {
      const tenantId = this.getTenantId(req);
      const userId = this.getUserId(req);
      const { id } = req.params;
      const { sessionId, registerId } = req.body;

      if (!sessionId || !registerId) {
        return this.validationError(res, {
          sessionId: sessionId ? undefined : 'Required',
          registerId: registerId ? undefined : 'Required',
        });
      }

      const result = await posOrderService.recallOrder(tenantId, id, sessionId, registerId, userId);

      if (!result.success) {
        return this.error(res, result.error.code, result.error.message);
      }

      return this.success(res, result.data);
    } catch (error) {
      next(error);
    }
  }

  // ==================== ADDITIONAL ENDPOINTS ====================

  /**
   * POST /pos/orders/:id/coupon - Apply coupon to order
   */
  async applyCoupon(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<Response | void> {
    try {
      const tenantId = this.getTenantId(req);
      const branchId = this.getBranchId(req);
      const { id } = req.params;
      const { couponCode } = req.body;

      if (!branchId) {
        return this.error(res, 'BRANCH_REQUIRED', 'X-Branch-ID header is required');
      }

      if (!couponCode) {
        return this.validationError(res, { couponCode: 'Required' });
      }

      const result = await posOrderService.applyCouponToOrder(tenantId, id, couponCode, branchId);

      if (!result.success) {
        return this.error(res, result.error.code, result.error.message);
      }

      return this.success(res, result.data);
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /pos/orders/:id/receipt/print - Mark receipt as printed
   */
  async markReceiptPrinted(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<Response | void> {
    try {
      const tenantId = this.getTenantId(req);
      const { id } = req.params;

      await posOrderService.markReceiptPrinted(tenantId, id);
      return this.success(res, { message: 'Receipt marked as printed' });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /pos/orders/:id/receipt/send - Send receipt by email
   */
  async sendReceipt(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<Response | void> {
    try {
      const tenantId = this.getTenantId(req);
      const { id } = req.params;
      const { email } = req.body;

      if (!email) {
        return this.validationError(res, { email: 'Required' });
      }

      const result = await posOrderService.sendReceiptEmail(tenantId, id, email);

      if (!result.success) {
        return this.error(res, result.error.code, result.error.message);
      }

      return this.success(res, { message: 'Receipt sent successfully' });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /pos/sessions/:id/stats - Get session statistics
   */
  async getSessionStats(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<Response | void> {
    try {
      const tenantId = this.getTenantId(req);
      const { id } = req.params;

      const stats = await posOrderService.getSessionStats(tenantId, id);
      return this.success(res, stats);
    } catch (error) {
      next(error);
    }
  }
}

export const posController = new POSController();
