import { Op } from 'sequelize';
import Member from '../models/Member.js';
import MemberOrder from '../models/MemberOrder.js';
import MemberOrderHistory from '../models/MemberOrderHistory.js';
import Club from '../models/Club.js';
import UserClub from '../models/UserClub.js';
import { checkAccess, getUserByToken } from '../utils/userUtils.js';

const ORDER_STATUSES = ['requested', 'ordered', 'arrived', 'handed_over'];

const normalizeAmount = (value) => {
  const parsed = Number.parseFloat(value ?? 0);
  if (!Number.isFinite(parsed)) {
    return 0;
  }
  return Math.max(0, Number(parsed.toFixed(2)));
};

const serializeOrder = (order) => {
  const plain = typeof order.toJSON === 'function' ? order.toJSON() : { ...order };
  const cost = normalizeAmount(plain.cost);
  const paidAmount = normalizeAmount(plain.paidAmount);
  const budget = normalizeAmount(plain.budget);
  const paidConfirmed = Boolean(plain.paidConfirmed);
  return {
    ...plain,
    cost,
    paidAmount,
    budget,
    paidConfirmed,
    openAmount: Math.max(0, Number((cost - paidAmount).toFixed(2)))
  };
};

const serializeHistoryEntry = (entry) => {
  const plain = typeof entry.toJSON === 'function' ? entry.toJSON() : { ...entry };
  const cost = normalizeAmount(plain.cost);
  const paidAmount = normalizeAmount(plain.paidAmount);
  const budget = normalizeAmount(plain.budget);
  const paidConfirmed = Boolean(plain.paidConfirmed);
  return {
    ...plain,
    cost,
    paidAmount,
    budget,
    paidConfirmed,
    openAmount: Math.max(0, Number((cost - paidAmount).toFixed(2)))
  };
};

class MemberOrderService {
  async _ensureMemberAccess(userToken, clubId, memberId) {
    await checkAccess(userToken, clubId);
    const member = await Member.findOne({
      where: {
        id: memberId,
        clubId
      }
    });
    if (!member) {
      throw new Error('Mitglied nicht gefunden');
    }
    return member;
  }

  async _createHistorySnapshot(order, transaction = null) {
    return MemberOrderHistory.create({
      memberOrderId: order.id,
      memberId: order.memberId,
      clubId: order.clubId,
      item: order.item,
      status: order.status,
      changedAt: new Date(),
      cost: normalizeAmount(order.cost),
      paidAmount: normalizeAmount(order.paidAmount),
      budget: normalizeAmount(order.budget),
      paidConfirmed: Boolean(order.paidConfirmed)
    }, transaction ? { transaction } : undefined);
  }

  async getMemberOrders(userToken, clubId, memberId) {
    await this._ensureMemberAccess(userToken, clubId, memberId);
    const orders = await MemberOrder.findAll({
      where: { clubId, memberId },
      include: [
        {
          model: MemberOrderHistory,
          as: 'historyEntries',
          required: false
        }
      ],
      order: [
        ['createdAt', 'DESC'],
        [{ model: MemberOrderHistory, as: 'historyEntries' }, 'changedAt', 'DESC']
      ]
    });

    return {
      status: 200,
      response: {
        success: true,
        orders: orders.map((order) => {
          const serialized = serializeOrder(order);
          serialized.historyEntries = (order.historyEntries || []).map(serializeHistoryEntry);
          return serialized;
        })
      }
    };
  }

  async createMemberOrder(userToken, clubId, memberId, payload) {
    await this._ensureMemberAccess(userToken, clubId, memberId);

    const item = String(payload?.item || '').trim();
    const status = ORDER_STATUSES.includes(payload?.status) ? payload.status : 'requested';
    const cost = normalizeAmount(payload?.cost);
    const paidAmount = normalizeAmount(payload?.paidAmount);
    const budget = normalizeAmount(payload?.budget);
    const paidConfirmed = Boolean(payload?.paidConfirmed);

    if (!item) {
      return {
        status: 400,
        response: { success: false, error: 'Artikel fehlt' }
      };
    }

    const order = await MemberOrder.create({
      memberId,
      clubId,
      item,
      status,
      orderDate: new Date(),
      statusDate: new Date(),
      cost,
      paidAmount,
      budget,
      paidConfirmed
    });

    await this._createHistorySnapshot(order);

    const fresh = await MemberOrder.findByPk(order.id, {
      include: [
        {
          model: MemberOrderHistory,
          as: 'historyEntries',
          required: false,
          order: [['changedAt', 'DESC']]
        }
      ]
    });

    const serialized = serializeOrder(fresh);
    serialized.historyEntries = (fresh.historyEntries || []).map(serializeHistoryEntry);

    return {
      status: 200,
      response: {
        success: true,
        order: serialized
      }
    };
  }

  async updateMemberOrder(userToken, clubId, memberId, orderId, payload) {
    await this._ensureMemberAccess(userToken, clubId, memberId);
    const order = await MemberOrder.findOne({
      where: {
        id: orderId,
        clubId,
        memberId
      }
    });

    if (!order) {
      return {
        status: 404,
        response: { success: false, error: 'Bestellung nicht gefunden' }
      };
    }

    let changed = false;
    const nextItem = payload?.item != null ? String(payload.item).trim() : order.item;
    const nextStatus = payload?.status && ORDER_STATUSES.includes(payload.status) ? payload.status : order.status;
    const nextCost = payload?.cost != null ? normalizeAmount(payload.cost) : normalizeAmount(order.cost);
    const nextPaidAmount = payload?.paidAmount != null ? normalizeAmount(payload.paidAmount) : normalizeAmount(order.paidAmount);
    const nextBudget = payload?.budget != null ? normalizeAmount(payload.budget) : normalizeAmount(order.budget);
    const nextPaidConfirmed = payload?.paidConfirmed != null ? Boolean(payload.paidConfirmed) : Boolean(order.paidConfirmed);

    if (nextItem && nextItem !== order.item) {
      order.item = nextItem;
      changed = true;
    }
    if (nextStatus !== order.status) {
      order.status = nextStatus;
      order.statusDate = new Date();
      changed = true;
    }
    if (nextCost !== normalizeAmount(order.cost)) {
      order.cost = nextCost;
      changed = true;
    }
    if (nextPaidAmount !== normalizeAmount(order.paidAmount)) {
      order.paidAmount = nextPaidAmount;
      changed = true;
    }
    if (nextBudget !== normalizeAmount(order.budget)) {
      order.budget = nextBudget;
      changed = true;
    }
    if (nextPaidConfirmed !== Boolean(order.paidConfirmed)) {
      order.paidConfirmed = nextPaidConfirmed;
      changed = true;
    }

    if (!changed) {
      const existing = await this.getMemberOrders(userToken, clubId, memberId);
      const current = existing.response.orders.find((entry) => String(entry.id) === String(orderId)) || null;
      return {
        status: 200,
        response: {
          success: true,
          order: current
        }
      };
    }

    await order.save();
    await this._createHistorySnapshot(order);

    const updated = await MemberOrder.findByPk(order.id, {
      include: [
        {
          model: MemberOrderHistory,
          as: 'historyEntries',
          required: false
        }
      ],
      order: [[{ model: MemberOrderHistory, as: 'historyEntries' }, 'changedAt', 'DESC']]
    });

    const serialized = serializeOrder(updated);
    serialized.historyEntries = (updated.historyEntries || []).map(serializeHistoryEntry);

    return {
      status: 200,
      response: {
        success: true,
        order: serialized
      }
    };
  }

  async getGlobalOrders(userToken) {
    const user = await getUserByToken(userToken);
    const userClubs = await UserClub.findAll({
      where: {
        userId: user.id,
        approved: true
      },
      attributes: ['clubId']
    });
    const clubIds = userClubs.map((entry) => entry.clubId).filter(Boolean);

    if (!clubIds.length) {
      return {
        status: 200,
        response: {
          success: true,
          orders: []
        }
      };
    }

    const orders = await MemberOrder.findAll({
      where: {
        clubId: {
          [Op.in]: clubIds
        }
      },
      include: [
        {
          model: Member,
          as: 'member',
          attributes: ['id', 'firstName', 'lastName']
        },
        {
          model: Club,
          as: 'club',
          attributes: ['id', 'name']
        },
        {
          model: MemberOrderHistory,
          as: 'historyEntries',
          required: false
        }
      ],
      order: [
        ['statusDate', 'DESC'],
        ['createdAt', 'DESC'],
        [{ model: MemberOrderHistory, as: 'historyEntries' }, 'changedAt', 'DESC']
      ]
    });

    return {
      status: 200,
      response: {
        success: true,
        orders: orders.map((order) => {
          const serialized = serializeOrder(order);
          serialized.historyEntries = (order.historyEntries || []).map(serializeHistoryEntry);
          return serialized;
        })
      }
    };
  }
}

export default new MemberOrderService();
