const mongoose = require('mongoose');
const { permissionFor, ENTITY_MODULE_MAP } = require('./accessControl');

const DEFAULT_ACTIONS = ['read', 'create', 'update', 'delete'];
const EXTRA_ENTITIES = ['role', 'permission', 'tenant', 'orgunit', 'auditlog', 'automation', 'workflows', 'ai'];
const WORKFLOW_PERMISSIONS = [
  ['ai.agent.publish', 'ai', 'agent', 'publish', 'publish AI agents'],
  ['ai.agent.run', 'ai', 'agent', 'run', 'run AI agents'],
  ['ai.agent.retry', 'ai', 'agent', 'retry', 'retry AI agent runs'],
  ['ai.agent.schedule.read', 'ai', 'agent', 'schedule.read', 'read AI agent schedules'],
  ['ai.agent.schedule.manage', 'ai', 'agent', 'schedule.manage', 'manage AI agent schedules'],
  ['ai.orchestration.run', 'ai', 'orchestration', 'run', 'run Hermes orchestration'],
  ['ai.orchestration.read', 'ai', 'orchestration', 'read', 'read Hermes orchestration state'],
  ['ai.notification.read', 'ai', 'notification', 'read', 'read AI and workflow notifications'],
  ['ai.notification.manage', 'ai', 'notification', 'manage', 'manage notification preferences'],
  ['ai.approval.decide', 'ai', 'approval', 'decide', 'approve sensitive agent actions'],
  ['ai.knowledge.manage', 'ai', 'knowledge', 'manage', 'manage agent knowledge sources'],
  ['ai.web.research', 'ai', 'web', 'research', 'research compliant public websites'],
  ['marketing.social.publish', 'marketing', 'social', 'publish', 'publish approved social content'],
  ['marketing.social.approve', 'marketing', 'social', 'approve', 'approve scheduled social content'],
  ['marketing.social.reschedule', 'marketing', 'social', 'reschedule', 'reschedule scheduled social content'],
  ['automations.calendar.read', 'automations', 'calendar', 'read', 'read automation calendar'],
  ['automations.calendar.manage', 'automations', 'calendar', 'manage', 'manage automation calendar'],
  ['automations.retry', 'automations', 'runner', 'retry', 'retry failed automation runs'],
  ['finance.agent.read', 'finance', 'agent', 'read', 'read finance data through agents'],
  ['finance.agent.allocate', 'finance', 'agent', 'allocate', 'approve agent budget allocation'],
  ['settings.admin.read', 'settings', 'admin', 'read', 'read user management console'],
  ['settings.admin.create', 'settings', 'admin', 'create', 'create admin users'],
  ['settings.admin.update', 'settings', 'admin', 'update', 'update admin users'],
  ['settings.admin.disable', 'settings', 'admin', 'disable', 'disable admin users'],
  ['settings.admin.invite', 'settings', 'admin', 'invite', 'invite admin users'],
  ['inventory.pos.read', 'inventory', 'pos', 'read', 'read point of sale workspace'],
  ['inventory.pos.create', 'inventory', 'pos', 'create', 'complete point of sale checkout'],
  ['inventory.pos.report.read', 'inventory', 'pos', 'report.read', 'read point of sale reports'],
  ['platform.form-builder.read', 'platform', 'form-builder', 'read', 'read shared form builder'],
  ['platform.form-builder.create', 'platform', 'form-builder', 'create', 'create shared forms'],
  ['platform.form-builder.update', 'platform', 'form-builder', 'update', 'update shared forms'],
];

const uniq = (items) => [...new Set(items)];

const getModel = (name, models) => models?.[name] || mongoose.model(name);

const buildPermissionCatalog = () => {
  const entities = uniq([...Object.keys(ENTITY_MODULE_MAP), ...EXTRA_ENTITIES]);

  return [...entities.flatMap((entity) =>
    DEFAULT_ACTIONS.map((action) => {
      const key = permissionFor(entity, action);
      const [module, entityName, actionName] = key.split('.');

      return {
        key,
        module,
        entity: entityName,
        action: actionName,
        description: `${actionName} ${entityName} records in ${module}`,
      };
    })
  ), ...WORKFLOW_PERMISSIONS.map(([key, module, entity, action, description]) => ({ key, module, entity, action, description }))];
};

const collectPermissions = (...prefixes) => {
  const catalog = buildPermissionCatalog();
  return catalog
    .map((permission) => permission.key)
    .filter((key) => prefixes.some((prefix) => key.startsWith(prefix)));
};

const DEFAULT_ROLE_DEFINITIONS = [
  {
    name: 'Admin',
    key: 'admin',
    permissions: ['*'],
    fieldPermissions: [],
    dataScope: 'tenant',
  },
  {
    name: 'Sales Rep',
    key: 'sales_rep',
    permissions: collectPermissions(
      'crm.contact.',
      'crm.company.',
      'crm.lead.',
      'crm.deal.',
      'crm.activity.',
      'crm.communicationlog.',
      'sales.offer.',
      'sales.quote.',
      'tasks.task.',
      'tasks.reminder.',
      'tasks.appointment.',
      'automations.workflow.'
    ),
    fieldPermissions: [
      { entity: 'deal', deniedFields: ['margin', 'commissionAmount'] },
      { entity: 'client', deniedFields: ['bankDetails', 'taxId'] },
    ],
    dataScope: 'own',
  },
  {
    name: 'Sales Manager',
    key: 'sales_manager',
    permissions: uniq([
      ...collectPermissions(
        'crm.contact.',
        'crm.company.',
        'crm.lead.',
        'crm.deal.',
        'crm.activity.',
        'crm.communicationlog.',
        'crm.pipelinestage.',
        'sales.offer.',
        'sales.quote.',
        'sales.salesorder.',
        'inventory.order.read',
        'sales.salestarget.',
        'sales.approvalrequest.',
        'sales.commission.',
        'tasks.task.',
        'tasks.reminder.',
        'tasks.appointment.',
        'automations.workflow.'
      ),
      'finance.invoice.read',
    ]),
    fieldPermissions: [{ entity: 'ledgerentry', deniedFields: ['accountNumber'] }],
    dataScope: 'team',
  },
  {
    name: 'Finance Officer',
    key: 'finance_officer',
    permissions: uniq([
      ...collectPermissions(
        'finance.account.',
        'finance.ledgerentry.',
        'finance.invoice.',
        'finance.payment.',
        'finance.expense.',
        'finance.expensecategory.',
        'finance.bill.',
        'finance.budget.',
        'finance.taxprofile.',
        'finance.taxes.',
        'finance.paymentmode.',
        'finance.currency.',
        'finance.pdfsetting.',
        'tasks.task.'
      ),
      'vendors.purchaseorder.read',
      'inventory.pos.read',
      'inventory.pos.create',
      'inventory.pos.report.read',
    ]),
    fieldPermissions: [],
    dataScope: 'tenant',
  },
  {
    name: 'Vendor Manager',
    key: 'vendor_manager',
    permissions: uniq([
      ...collectPermissions(
        'vendors.vendor.',
        'inventory.supplier.',
        'vendors.vendorcontract.',
        'vendors.purchaseorder.',
        'vendors.vendorscorecard.',
        'tasks.task.'
      ),
      'finance.bill.read',
      'finance.bill.update',
      'finance.payment.read',
    ]),
    fieldPermissions: [{ entity: 'vendor', deniedFields: ['bankDetails'] }],
    dataScope: 'tenant',
  },
  {
    name: 'Marketing Manager',
    key: 'marketing_manager',
    permissions: uniq([
      ...collectPermissions(
        'marketing.campaign.',
        'marketing.audiencesegment.',
        'marketing.landingform.',
        'marketing.campaignevent.',
        'marketing.socialpost.',
        'marketing.contentasset.',
        'platform.publicform.',
        'platform.formdefinition.',
        'platform.formsubmission.',
        'tasks.task.',
        'ai.studio.',
        'ai.agentdefinition.',
        'ai.agentrun.',
        'ai.agentapproval.',
        'ai.knowledgesource.',
        'ai.brandprofile.',
        'marketing.socialconnection.'
      ),
      'ai.agent.publish',
      'ai.agent.run',
      'ai.agent.retry',
      'ai.agent.schedule.read',
      'ai.agent.schedule.manage',
      'ai.orchestration.run',
      'ai.orchestration.read',
      'ai.notification.read',
      'ai.notification.manage',
      'ai.approval.decide',
      'ai.knowledge.manage',
      'ai.web.research',
      'marketing.social.publish',
      'marketing.social.approve',
      'marketing.social.reschedule',
      'automations.calendar.read',
      'automations.calendar.manage',
      'automations.retry',
      'platform.form-builder.read',
      'platform.form-builder.create',
      'platform.form-builder.update',
      'crm.contact.read',
      'crm.company.read',
      'crm.lead.read',
    ]),
    fieldPermissions: [],
    dataScope: 'tenant',
  },
];

const financeRole = DEFAULT_ROLE_DEFINITIONS.find((role) => role.key === 'finance_officer');
financeRole.permissions = uniq([...financeRole.permissions, 'finance.agent.read', 'finance.agent.allocate']);

const seedTenantAccessControl = async ({ tenantId, actorId = null, models = {} }) => {
  const Permission = getModel('Permission', models);
  const Role = getModel('Role', models);

  const permissions = [];
  for (const permission of buildPermissionCatalog()) {
    const record = await Permission.findOneAndUpdate(
      { tenant: tenantId, key: permission.key },
      {
        $set: {
          ...permission,
          tenant: tenantId,
          removed: false,
        },
        $setOnInsert: {
          createdBy: actorId,
          assignedTo: actorId,
        },
      },
      { upsert: true, new: true, setDefaultsOnInsert: true }
    );
    permissions.push(record);
  }

  const roles = [];
  for (const role of DEFAULT_ROLE_DEFINITIONS) {
    const record = await Role.findOneAndUpdate(
      { tenant: tenantId, key: role.key },
      {
        $set: {
          ...role,
          tenant: tenantId,
          removed: false,
        },
        $setOnInsert: {
          createdBy: actorId,
          assignedTo: actorId,
        },
      },
      { upsert: true, new: true, setDefaultsOnInsert: true }
    );
    roles.push(record);
  }

  return { permissions, roles };
};

module.exports = {
  buildPermissionCatalog,
  DEFAULT_ROLE_DEFINITIONS,
  seedTenantAccessControl,
};
