import UserClub from '../models/UserClub.js';
import User from '../models/User.js';
import ClubRole from '../models/ClubRole.js';
import ClubUserRole from '../models/ClubUserRole.js';

const ROLE_PERMISSIONS = {
  admin: {
    diary: { read: true, write: true, delete: true },
    members: { read: true, write: true, delete: true },
    requests: { read: true, write: true, delete: true },
    tasks: { read: true, write: true, delete: true },
    teams: { read: true, write: true, delete: true },
    schedule: { read: true, write: true, delete: true },
    tournaments: { read: true, write: true, delete: true },
    statistics: { read: true, write: true },
    finance_accounts: { read: true, write: true, delete: true },
    finance_invoices: { read: true, write: true, delete: true },
    history: { read: true, write: true },
    archive: { read: true, write: true },
    settings: { read: true, write: true },
    permissions: { read: true, write: true },
    approvals: { read: true, write: true },
    communication: { read: true, write: true, delete: true },
    mytischtennis_admin: { read: true, write: true },
    predefined_activities: { read: true, write: true, delete: true },
  },
  trainer: {
    diary: { read: true, write: true, delete: true },
    members: { read: true, write: true, delete: false },
    requests: { read: true, write: true, delete: false },
    tasks: { read: true, write: true, delete: false },
    teams: { read: true, write: true, delete: false },
    schedule: { read: true, write: false, delete: false },
    tournaments: { read: true, write: true, delete: false },
    statistics: { read: true, write: false },
    finance_accounts: { read: false, write: false, delete: false },
    finance_invoices: { read: false, write: false, delete: false },
    history: { read: true, write: false },
    archive: { read: false, write: false },
    settings: { read: false, write: false },
    permissions: { read: false, write: false },
    approvals: { read: false, write: false },
    communication: { read: true, write: true, delete: false },
    mytischtennis_admin: { read: false, write: false },
    predefined_activities: { read: true, write: true, delete: true },
  },
  team_manager: {
    diary: { read: false, write: false, delete: false },
    members: { read: true, write: false, delete: false },
    requests: { read: true, write: false, delete: false },
    tasks: { read: true, write: true, delete: false },
    teams: { read: true, write: true, delete: false },
    schedule: { read: true, write: true, delete: false },
    tournaments: { read: true, write: false, delete: false },
    statistics: { read: true, write: false },
    finance_accounts: { read: false, write: false, delete: false },
    finance_invoices: { read: false, write: false, delete: false },
    history: { read: true, write: false },
    archive: { read: false, write: false },
    settings: { read: false, write: false },
    permissions: { read: false, write: false },
    approvals: { read: false, write: false },
    communication: { read: true, write: false, delete: false },
    mytischtennis_admin: { read: false, write: false },
    predefined_activities: { read: false, write: false, delete: false },
  },
  tournament_manager: {
    diary: { read: false, write: false, delete: false },
    members: { read: true, write: false, delete: false },
    requests: { read: true, write: false, delete: false },
    tasks: { read: true, write: false, delete: false },
    teams: { read: false, write: false, delete: false },
    schedule: { read: false, write: false, delete: false },
    tournaments: { read: true, write: true, delete: false },
    statistics: { read: true, write: false },
    finance_accounts: { read: false, write: false, delete: false },
    finance_invoices: { read: false, write: false, delete: false },
    history: { read: true, write: false },
    archive: { read: false, write: false },
    settings: { read: false, write: false },
    permissions: { read: false, write: false },
    approvals: { read: false, write: false },
    communication: { read: true, write: false, delete: false },
    mytischtennis_admin: { read: false, write: false },
    predefined_activities: { read: false, write: false, delete: false },
  },
  cashier: {
    diary: { read: false, write: false, delete: false },
    members: { read: true, write: false, delete: false },
    requests: { read: true, write: false, delete: false },
    tasks: { read: true, write: true, delete: false },
    teams: { read: false, write: false, delete: false },
    schedule: { read: false, write: false, delete: false },
    tournaments: { read: false, write: false, delete: false },
    statistics: { read: true, write: false },
    finance_accounts: { read: true, write: true, delete: false },
    finance_invoices: { read: true, write: true, delete: false },
    history: { read: true, write: false },
    archive: { read: true, write: false },
    settings: { read: false, write: false },
    permissions: { read: false, write: false },
    approvals: { read: false, write: false },
    communication: { read: true, write: false, delete: false },
    mytischtennis_admin: { read: false, write: false },
    predefined_activities: { read: false, write: false, delete: false },
  },
  member: {
    diary: { read: false, write: false, delete: false },
    members: { read: false, write: false, delete: false },
    requests: { read: false, write: false, delete: false },
    tasks: { read: false, write: false, delete: false },
    teams: { read: false, write: false, delete: false },
    schedule: { read: false, write: false, delete: false },
    tournaments: { read: false, write: false, delete: false },
    statistics: { read: true, write: false },
    finance_accounts: { read: false, write: false, delete: false },
    finance_invoices: { read: false, write: false, delete: false },
    history: { read: false, write: false },
    archive: { read: false, write: false },
    settings: { read: false, write: false },
    permissions: { read: false, write: false },
    approvals: { read: false, write: false },
    communication: { read: false, write: false, delete: false },
    mytischtennis_admin: { read: false, write: false },
    predefined_activities: { read: false, write: false, delete: false },
  },
};

const DEFAULT_ROLE_TEMPLATES = [
  { roleKey: 'admin', name: 'Administrator', description: 'Vollzugriff auf alle Funktionen', permissions: ROLE_PERMISSIONS.admin, sortOrder: 10 },
  { roleKey: 'trainer', name: 'Trainer', description: 'Kann Trainingseinheiten, Mitglieder und Teams verwalten', permissions: ROLE_PERMISSIONS.trainer, sortOrder: 20 },
  { roleKey: 'team_manager', name: 'Mannschaftsführer', description: 'Kann Teams und Spielpläne verwalten', permissions: ROLE_PERMISSIONS.team_manager, sortOrder: 30 },
  { roleKey: 'tournament_manager', name: 'Turnierleiter', description: 'Kann Turniere verwalten', permissions: ROLE_PERMISSIONS.tournament_manager, sortOrder: 40 },
  { roleKey: 'cashier', name: 'Kassierer', description: 'Kann Konten, Rechnungen und finanznahe Aufgaben verwalten', permissions: ROLE_PERMISSIONS.cashier, sortOrder: 45 },
  { roleKey: 'member', name: 'Mitglied', description: 'Kann nur freigegebene Vereinsbereiche ansehen', permissions: ROLE_PERMISSIONS.member, sortOrder: 50 },
];

function cloneValue(value) {
  return JSON.parse(JSON.stringify(value));
}

function normalizePermissions(value) {
  if (!value) return {};
  if (typeof value === 'string') {
    try {
      return JSON.parse(value);
    } catch (_error) {
      return {};
    }
  }
  return cloneValue(value);
}

function slugifyRoleKey(value) {
  return String(value || '')
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '_')
    .replace(/^_+|_+$/g, '')
    .slice(0, 64) || 'rolle';
}

class PermissionService {
  mergePermissions(basePermissions = {}, extraPermissions = {}) {
    const merged = cloneValue(basePermissions);
    const normalizedExtra = normalizePermissions(extraPermissions);

    for (const resource of Object.keys(normalizedExtra)) {
      merged[resource] = {
        ...(merged[resource] || {}),
        ...(normalizedExtra[resource] || {}),
      };
    }

    return merged;
  }

  getPermissionStructure() {
    return {
      diary: { label: 'Trainingstagebuch', actions: ['read', 'write', 'delete'] },
      members: { label: 'Mitglieder', actions: ['read', 'write', 'delete'] },
      requests: { label: 'Anfragen', actions: ['read', 'write', 'delete'] },
      tasks: { label: 'Aufgaben', actions: ['read', 'write', 'delete'] },
      teams: { label: 'Teams', actions: ['read', 'write', 'delete'] },
      schedule: { label: 'Spielpläne', actions: ['read', 'write', 'delete'] },
      tournaments: { label: 'Turniere', actions: ['read', 'write', 'delete'] },
      statistics: { label: 'Statistiken', actions: ['read', 'write'] },
      finance_accounts: { label: 'Konten', actions: ['read', 'write', 'delete'] },
      finance_invoices: { label: 'Rechnungen', actions: ['read', 'write', 'delete'] },
      history: { label: 'Historie', actions: ['read', 'write'] },
      archive: { label: 'Archiv', actions: ['read', 'write'] },
      settings: { label: 'Einstellungen', actions: ['read', 'write'] },
      permissions: { label: 'Berechtigungsverwaltung', actions: ['read', 'write'] },
      approvals: { label: 'Freigaben (Mitgliedsanträge)', actions: ['read', 'write'] },
      communication: { label: 'Kommunikation', actions: ['read', 'write', 'delete'] },
      mytischtennis_admin: { label: 'MyTischtennis Admin', actions: ['read', 'write'] },
      predefined_activities: { label: 'Vordefinierte Aktivitäten', actions: ['read', 'write', 'delete'] },
    };
  }

  getAvailableRoles() {
    return DEFAULT_ROLE_TEMPLATES.map((role) => ({
      value: role.roleKey,
      label: role.name,
      description: role.description,
      isSystemRole: true,
    }));
  }

  isMissingRoleTableError(error) {
    return error?.original?.code === 'ER_NO_SUCH_TABLE'
      && /club_roles|club_user_roles/.test(String(error?.original?.sqlMessage || ''));
  }

  async ensureDefaultRoles(clubId) {
    const createdRoles = [];
    for (const template of DEFAULT_ROLE_TEMPLATES) {
      const [role, created] = await ClubRole.findOrCreate({
        where: { clubId, roleKey: template.roleKey },
        defaults: {
          clubId,
          roleKey: template.roleKey,
          name: template.name,
          description: template.description,
          permissions: template.permissions,
          isSystemRole: true,
          sortOrder: template.sortOrder,
        },
      });

      if (!created && role.isSystemRole) {
        const mergedPermissions = this.mergePermissions(template.permissions, role.permissions);
        await role.update({
          name: role.name || template.name,
          description: role.description || template.description,
          permissions: mergedPermissions,
          sortOrder: role.sortOrder || template.sortOrder,
        });
      }
      createdRoles.push(role);
    }
    return createdRoles;
  }

  async getRoleAssignments(clubId, userIds = null) {
    const where = { clubId };
    if (Array.isArray(userIds)) {
      where.userId = userIds;
    }

    return ClubUserRole.findAll({
      where,
      include: [{
        model: ClubRole,
        as: 'role',
      }],
      order: [
        ['isPrimary', 'DESC'],
        [{ model: ClubRole, as: 'role' }, 'sortOrder', 'ASC'],
        [{ model: ClubRole, as: 'role' }, 'name', 'ASC'],
      ],
    });
  }

  buildLegacyPermissionPayload(userClub) {
    if (userClub.isOwner) {
      return {
        role: 'admin',
        roles: [{ roleKey: 'admin', name: 'Administrator', isPrimary: true, isSystemRole: true }],
        isOwner: true,
        isAdmin: true,
        permissions: cloneValue(ROLE_PERMISSIONS.admin),
      };
    }

    const primaryRole = userClub.role || 'member';
    const effectivePermissions = this.mergePermissions(
      ROLE_PERMISSIONS[primaryRole] || ROLE_PERMISSIONS.member,
      userClub.permissions
    );

    return {
      role: primaryRole,
      roles: [{
        roleKey: primaryRole,
        name: DEFAULT_ROLE_TEMPLATES.find((role) => role.roleKey === primaryRole)?.name || primaryRole,
        isPrimary: true,
        isSystemRole: true,
      }],
      isOwner: false,
      isAdmin: primaryRole === 'admin',
      permissions: effectivePermissions,
    };
  }

  buildRolePermissionPayload(userClub, assignments) {
    if (userClub.isOwner) {
      return {
        role: 'admin',
        roles: [{ roleKey: 'admin', name: 'Administrator', isPrimary: true, isSystemRole: true }],
        isOwner: true,
        isAdmin: true,
        permissions: cloneValue(ROLE_PERMISSIONS.admin),
      };
    }

    const normalizedAssignments = assignments
      .filter((assignment) => assignment?.role)
      .map((assignment) => ({
        id: assignment.role.id,
        roleKey: assignment.role.roleKey,
        name: assignment.role.name,
        description: assignment.role.description,
        isSystemRole: Boolean(assignment.role.isSystemRole),
        isPrimary: Boolean(assignment.isPrimary),
        assignedAt: assignment.createdAt || null,
        assignmentUpdatedAt: assignment.updatedAt || null,
        roleCreatedAt: assignment.role.createdAt || null,
        roleUpdatedAt: assignment.role.updatedAt || null,
        permissions: normalizePermissions(assignment.role.permissions),
      }));

    if (normalizedAssignments.length === 0) {
      return this.buildLegacyPermissionPayload(userClub);
    }

    const primaryRole = normalizedAssignments.find((role) => role.isPrimary) || normalizedAssignments[0];
    const rolePermissions = normalizedAssignments.reduce(
      (accumulator, role) => this.mergePermissions(accumulator, role.permissions),
      {}
    );
    const effectivePermissions = this.mergePermissions(rolePermissions, userClub.permissions);

    return {
      role: normalizedAssignments.some((role) => role.roleKey === 'admin') ? 'admin' : primaryRole.roleKey,
      roles: normalizedAssignments.map(({ permissions, ...role }) => role),
      isOwner: false,
      isAdmin: normalizedAssignments.some((role) => role.roleKey === 'admin'),
      permissions: effectivePermissions,
    };
  }

  async getUserClubPermissions(userId, clubId) {
    const userClub = await UserClub.findOne({
      where: { userId, clubId, approved: true },
    });

    if (!userClub) {
      return null;
    }

    try {
      await this.ensureDefaultRoles(clubId);
      const assignments = await this.getRoleAssignments(clubId, [userId]);
      return this.buildRolePermissionPayload(userClub, assignments);
    } catch (error) {
      if (this.isMissingRoleTableError(error)) {
        return this.buildLegacyPermissionPayload(userClub);
      }
      throw error;
    }
  }

  async hasPermission(userId, clubId, resource, action) {
    const userPermissions = await this.getUserClubPermissions(userId, clubId);
    if (!userPermissions) {
      return false;
    }
    if (userPermissions.isOwner) {
      return true;
    }
    if (resource === 'mytischtennis') {
      return true;
    }
    return userPermissions.permissions?.[resource]?.[action] === true;
  }

  async setUserRole(userId, clubId, roleKey, updatedByUserId) {
    await this.ensureDefaultRoles(clubId);
    const role = await ClubRole.findOne({ where: { clubId, roleKey } });
    if (!role) {
      throw new Error('Ungültige Rolle');
    }
    return this.setUserRoles(userId, clubId, [role.id], updatedByUserId);
  }

  async setUserRoles(userId, clubId, roleIds, updatedByUserId) {
    const canManagePermissions = await this.hasPermission(updatedByUserId, clubId, 'permissions', 'write');
    if (!canManagePermissions) {
      throw new Error('Keine Berechtigung zum Ändern von Rollen');
    }

    const targetUserClub = await UserClub.findOne({ where: { userId, clubId } });
    if (!targetUserClub) {
      throw new Error('Benutzer ist kein Mitglied dieses Clubs');
    }
    if (targetUserClub.isOwner) {
      throw new Error('Die Rollen des Club-Erstellers können nicht geändert werden');
    }

    await this.ensureDefaultRoles(clubId);
    const normalizedRoleIds = [...new Set((roleIds || []).map((id) => Number(id)).filter(Boolean))];
    const roles = normalizedRoleIds.length > 0
      ? await ClubRole.findAll({ where: { clubId, id: normalizedRoleIds } })
      : [];

    if (roles.length !== normalizedRoleIds.length) {
      throw new Error('Mindestens eine Rolle gehört nicht zu diesem Verein');
    }

    try {
      await ClubUserRole.destroy({ where: { clubId, userId } });
      if (roles.length > 0) {
        await ClubUserRole.bulkCreate(roles.map((role, index) => ({
          clubId,
          userId,
          clubRoleId: role.id,
          isPrimary: index === 0,
        })));
      }
      await targetUserClub.update({ role: roles[0]?.roleKey || 'member' });
    } catch (error) {
      if (this.isMissingRoleTableError(error)) {
        await targetUserClub.update({ role: roles[0]?.roleKey || 'member' });
      } else {
        throw error;
      }
    }

    return { success: true, message: 'Rollen erfolgreich aktualisiert' };
  }

  async setCustomPermissions(userId, clubId, customPermissions, updatedByUserId) {
    const canManagePermissions = await this.hasPermission(updatedByUserId, clubId, 'permissions', 'write');
    if (!canManagePermissions) {
      throw new Error('Keine Berechtigung zum Ändern von Berechtigungen');
    }

    const targetUserClub = await UserClub.findOne({ where: { userId, clubId } });
    if (!targetUserClub) {
      throw new Error('Benutzer ist kein Mitglied dieses Clubs');
    }
    if (targetUserClub.isOwner) {
      throw new Error('Die Berechtigungen des Club-Erstellers können nicht geändert werden');
    }

    await targetUserClub.update({ permissions: customPermissions });
    return { success: true, message: 'Berechtigungen erfolgreich aktualisiert' };
  }

  async setUserStatus(userId, clubId, approved, updatedByUserId) {
    const canManagePermissions = await this.hasPermission(updatedByUserId, clubId, 'permissions', 'write');
    if (!canManagePermissions) {
      throw new Error('Keine Berechtigung zum Ändern des Status');
    }

    const targetUserClub = await UserClub.findOne({ where: { userId, clubId } });
    if (!targetUserClub) {
      throw new Error('Benutzer ist kein Mitglied dieses Clubs');
    }
    if (targetUserClub.isOwner) {
      throw new Error('Der Status des Club-Erstellers kann nicht geändert werden');
    }

    await targetUserClub.update({ approved });
    return { success: true, message: approved ? 'Benutzer erfolgreich aktiviert' : 'Benutzer erfolgreich deaktiviert' };
  }

  async getClubMembersWithPermissions(clubId, requestingUserId) {
    const canReadPermissions = await this.hasPermission(requestingUserId, clubId, 'permissions', 'read');
    if (!canReadPermissions) {
      throw new Error('Keine Berechtigung zum Anzeigen von Berechtigungen');
    }

    const userClubs = await UserClub.findAll({
      where: { clubId },
      include: [{ model: User, as: 'user', attributes: ['id', 'email'] }],
      order: [[{ model: User, as: 'user' }, 'email', 'ASC']],
    });

    try {
      await this.ensureDefaultRoles(clubId);
      const assignments = await this.getRoleAssignments(clubId, userClubs.map((entry) => entry.userId));
      const assignmentsByUserId = assignments.reduce((accumulator, assignment) => {
        const key = Number(assignment.userId);
        if (!accumulator[key]) {
          accumulator[key] = [];
        }
        accumulator[key].push(assignment);
        return accumulator;
      }, {});

      return userClubs.map((userClub) => {
        const payload = this.buildRolePermissionPayload(userClub, assignmentsByUserId[Number(userClub.userId)] || []);
        return {
          userId: userClub.userId,
          user: userClub.user,
          role: payload.role,
          roles: payload.roles,
          isAdmin: payload.isAdmin,
          isOwner: userClub.isOwner,
          approved: userClub.approved,
          createdAt: userClub.createdAt,
          updatedAt: userClub.updatedAt,
          permissions: normalizePermissions(userClub.permissions),
          effectivePermissions: payload.permissions,
        };
      });
    } catch (error) {
      if (this.isMissingRoleTableError(error)) {
        return userClubs.map((userClub) => {
          const payload = this.buildLegacyPermissionPayload(userClub);
          return {
            userId: userClub.userId,
            user: userClub.user,
            role: payload.role,
            roles: payload.roles,
            isAdmin: payload.isAdmin,
            isOwner: userClub.isOwner,
            approved: userClub.approved,
            createdAt: userClub.createdAt,
            updatedAt: userClub.updatedAt,
            permissions: normalizePermissions(userClub.permissions),
            effectivePermissions: payload.permissions,
          };
        });
      }
      throw error;
    }
  }

  async getClubRoles(clubId, requestingUserId) {
    const canReadPermissions = await this.hasPermission(requestingUserId, clubId, 'permissions', 'read');
    if (!canReadPermissions) {
      throw new Error('Keine Berechtigung zum Anzeigen von Rollen');
    }

    await this.ensureDefaultRoles(clubId);
    return ClubRole.findAll({
      where: { clubId },
      order: [['sortOrder', 'ASC'], ['name', 'ASC']],
    });
  }

  async createClubRole(clubId, payload, requestingUserId) {
    const canManagePermissions = await this.hasPermission(requestingUserId, clubId, 'permissions', 'write');
    if (!canManagePermissions) {
      throw new Error('Keine Berechtigung zum Anlegen von Rollen');
    }

    const baseKey = slugifyRoleKey(payload.roleKey || payload.name);
    const existingKeys = new Set((await ClubRole.findAll({
      where: { clubId },
      attributes: ['roleKey'],
    })).map((role) => role.roleKey));
    let roleKey = baseKey;
    let suffix = 2;
    while (existingKeys.has(roleKey)) {
      roleKey = `${baseKey}_${suffix++}`;
    }

    const role = await ClubRole.create({
      clubId,
      roleKey,
      name: String(payload.name || '').trim(),
      description: String(payload.description || '').trim() || null,
      permissions: normalizePermissions(payload.permissions),
      isSystemRole: false,
      sortOrder: Number(payload.sortOrder) || 100,
    });

    return role;
  }

  async updateClubRole(clubId, roleId, payload, requestingUserId) {
    const canManagePermissions = await this.hasPermission(requestingUserId, clubId, 'permissions', 'write');
    if (!canManagePermissions) {
      throw new Error('Keine Berechtigung zum Ändern von Rollen');
    }

    const role = await ClubRole.findOne({ where: { id: roleId, clubId } });
    if (!role) {
      throw new Error('Rolle nicht gefunden');
    }

    await role.update({
      name: String(payload.name || role.name).trim(),
      description: payload.description === undefined ? role.description : (String(payload.description || '').trim() || null),
      permissions: payload.permissions === undefined ? role.permissions : normalizePermissions(payload.permissions),
      sortOrder: payload.sortOrder === undefined ? role.sortOrder : Number(payload.sortOrder) || 100,
    });

    return role;
  }

  async deleteClubRole(clubId, roleId, requestingUserId) {
    const canManagePermissions = await this.hasPermission(requestingUserId, clubId, 'permissions', 'write');
    if (!canManagePermissions) {
      throw new Error('Keine Berechtigung zum Löschen von Rollen');
    }

    const role = await ClubRole.findOne({ where: { id: roleId, clubId } });
    if (!role) {
      throw new Error('Rolle nicht gefunden');
    }
    if (role.isSystemRole) {
      throw new Error('Systemrollen können nicht gelöscht werden');
    }

    await ClubUserRole.destroy({ where: { clubId, clubRoleId: roleId } });
    await role.destroy();
    return { success: true, message: 'Rolle erfolgreich gelöscht' };
  }

  async setClubOwner(userId, clubId) {
    const userClub = await UserClub.findOne({ where: { userId, clubId } });
    if (!userClub) {
      throw new Error('UserClub relationship not found');
    }

    await userClub.update({ isOwner: true, role: 'admin', approved: true });
    try {
      await this.ensureDefaultRoles(clubId);
      const adminRole = await ClubRole.findOne({ where: { clubId, roleKey: 'admin' } });
      if (adminRole) {
        await ClubUserRole.destroy({ where: { clubId, userId } });
        await ClubUserRole.create({
          clubId,
          userId,
          clubRoleId: adminRole.id,
          isPrimary: true,
        });
      }
    } catch (error) {
      if (!this.isMissingRoleTableError(error)) {
        throw error;
      }
    }
  }
}

export default new PermissionService();
