const bcrypt = require('bcrypt');
const db = require('../db/models');

const DEFAULT_PROFILE = {
  businessName: 'VORTA Nexus Demo',
  trustScore: 50,
  verified: false,
  walletBalance: 1850000,
  geoseekRadiusKm: 7,
  aiQuota: 32,
  phone: '',
  phoneVerified: false,
};

const DEFAULT_SETTINGS = {
  trustAutoSync: true,
  marketplaceMode: 'Demo Aktif',
  riskReview: 'Medium',
};

const DEFAULT_MODULES = [
  {
    id: 'business-chat',
    icon: '💬',
    name: 'Chat Bisnis',
    description: 'Inbox komersial untuk buyer, seller, partner, dan tim internal.',
    metric: '128 percakapan',
    status: 'Online',
    trustImpact: 2,
    launches: 0,
  },
  {
    id: 'marketplace',
    icon: '🛒',
    name: 'Marketplace',
    description: 'Etalase produk, seller score, katalog cepat, dan aktivitas listing.',
    metric: '24 listing aktif',
    status: 'Online',
    trustImpact: 3,
    launches: 0,
  },
  {
    id: 'wallet',
    icon: '💳',
    name: 'Wallet',
    description: 'Saldo, pembayaran, reward, dan settlement untuk pengguna VORTA.',
    metric: 'Rp1,85 jt saldo',
    status: 'Online',
    trustImpact: 2,
    launches: 0,
  },
  {
    id: 'geoseek',
    icon: '🌍',
    name: 'GeoSeek',
    description: 'Pencarian peluang, mitra, dan kebutuhan berdasarkan lokasi.',
    metric: '7 km radius',
    status: 'Beta',
    trustImpact: 1,
    launches: 0,
  },
  {
    id: 'vorta-ai',
    icon: '🤖',
    name: 'VORTA AI',
    description: 'Asisten AI untuk ide bisnis, analisis produk, dan rekomendasi aksi.',
    metric: '32 prompt tersisa',
    status: 'Siap',
    trustImpact: 2,
    launches: 0,
  },
  {
    id: 'trust-system',
    icon: '⭐',
    name: 'Trust System',
    description: 'Nexus Trust Score, validasi profil, reputasi transaksi, dan sinyal risiko.',
    metric: 'Score dasar 50',
    status: 'Aktif',
    trustImpact: 4,
    launches: 0,
  },
];

const DEFAULT_PRODUCTS = [
  {
    id: 'VU-PRD-001',
    name: 'VORTA Starter Kit',
    price: 149000,
    seller: 'Nexus Seller Lab',
    category: 'Digital Toolkit',
    stock: 120,
  },
  {
    id: 'VU-PRD-002',
    name: 'GeoSeek Local Leads',
    price: 99000,
    seller: 'VORTA Data Market',
    category: 'Lead Pack',
    stock: 45,
  },
  {
    id: 'VU-PRD-003',
    name: 'Trust Boost Verification',
    price: 199000,
    seller: 'VORTA Trust Desk',
    category: 'Verification',
    stock: 18,
  },
];

const DEFAULT_LEDGER = [
  {
    id: 'VU-LOG-1',
    title: 'Nexus Trust Score aktif',
    detail: 'Baseline score dimulai dari 50 dan tersimpan di database PostgreSQL.',
    time: 'Baru saja',
    tone: 'success',
  },
  {
    id: 'VU-LOG-2',
    title: 'Marketplace demo tersambung',
    detail: 'Produk awal siap diuji tanpa membuat aplikasi Express/SQLite terpisah.',
    time: 'Baru saja',
    tone: 'info',
  },
];

const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
const cloneJson = (value) => JSON.parse(JSON.stringify(value));

const buildHttpError = (message, code = 400) => {
  const error = new Error(message);
  error.code = code;
  return error;
};

const toNumber = (value, fallback = 0) => {
  const parsed = Number(value);
  return Number.isFinite(parsed) ? parsed : fallback;
};

const getOrganizationId = (currentUser) => currentUser?.organizationId || currentUser?.organizationsId || null;

const makeImportHash = (scope, currentUser) => {
  const userId = currentUser?.id || 'anonymous';
  const organizationId = getOrganizationId(currentUser) || 'global';
  return `vorta-universe-${organizationId}-${userId}-${scope}`;
};

const nowLabel = () => new Intl.DateTimeFormat('id-ID', {
  dateStyle: 'medium',
  timeStyle: 'short',
}).format(new Date());

const getDisplayName = (currentUser) => {
  const firstName = currentUser?.firstName || '';
  const lastName = currentUser?.lastName || '';
  const fullName = `${firstName} ${lastName}`.trim();

  return fullName || currentUser?.email || 'Founder VORTA';
};

const getScopedWhere = (currentUser) => ({
  userId: currentUser?.id || null,
  organizationsId: getOrganizationId(currentUser),
});

const normalizePhone = (phone) => String(phone || '').replace(/[^0-9+]/g, '').trim();

const makeOtp = () => String(Math.floor(100000 + Math.random() * 900000));


class VortaUniverseService {
  static normalizeState(record) {
    const profile = {
      ...cloneJson(DEFAULT_PROFILE),
      ...(record.profile || {}),
    };

    const modules = Array.isArray(record.modules) && record.modules.length
      ? record.modules
      : cloneJson(DEFAULT_MODULES);
    const products = Array.isArray(record.products) && record.products.length
      ? record.products
      : cloneJson(DEFAULT_PRODUCTS);
    const ledger = Array.isArray(record.ledger) && record.ledger.length
      ? record.ledger
      : cloneJson(DEFAULT_LEDGER);

    return {
      profile: {
        ...profile,
        trustScore: clamp(toNumber(profile.trustScore, DEFAULT_PROFILE.trustScore), 0, 100),
        verified: Boolean(profile.verified || toNumber(profile.trustScore, 0) >= 80),
      },
      modules,
      products,
      ledger,
      settings: {
        ...cloneJson(DEFAULT_SETTINGS),
        ...(record.settings || {}),
      },
      persistedAt: new Date().toISOString(),
    };
  }

  static async ensureState(currentUser, transaction) {
    const importHash = makeImportHash('state', currentUser);
    const where = { importHash };
    const existing = await db.vorta_universe_states.findOne({ where, transaction });

    if (existing) {
      return existing;
    }

    return db.vorta_universe_states.create(
      {
        userId: currentUser?.id || null,
        organizationsId: getOrganizationId(currentUser),
        profile: cloneJson(DEFAULT_PROFILE),
        modules: cloneJson(DEFAULT_MODULES),
        products: cloneJson(DEFAULT_PRODUCTS),
        ledger: cloneJson(DEFAULT_LEDGER),
        settings: cloneJson(DEFAULT_SETTINGS),
        importHash,
        createdById: currentUser?.id || null,
        updatedById: currentUser?.id || null,
      },
      { transaction },
    );
  }

  static async getState(currentUser) {
    const state = await db.sequelize.transaction(async (transaction) => this.ensureState(currentUser, transaction));
    return this.normalizeState(state);
  }

  static async resetState(currentUser) {
    await db.sequelize.transaction(async (transaction) => {
      const state = await this.ensureState(currentUser, transaction);

      await state.update(
        {
          profile: cloneJson(DEFAULT_PROFILE),
          modules: cloneJson(DEFAULT_MODULES),
          products: cloneJson(DEFAULT_PRODUCTS),
          ledger: this.addLedgerItem(cloneJson(DEFAULT_LEDGER), {
            title: 'State VORTA di-reset',
            detail: 'Profile, module, produk, setting, dan ledger kembali ke data demo awal.',
            tone: 'warning',
          }),
          settings: cloneJson(DEFAULT_SETTINGS),
          updatedById: currentUser?.id || null,
        },
        { transaction },
      );
    });

    return this.getState(currentUser);
  }

  static addLedgerItem(ledger, item) {
    return [
      {
        id: `VU-LOG-${Date.now()}`,
        time: nowLabel(),
        tone: 'info',
        ...item,
      },
      ...(Array.isArray(ledger) ? ledger : []),
    ].slice(0, 12);
  }

  static async updateTrustScore(currentUser, payload) {
    const requestedScore = toNumber(payload.score, Number.NaN);

    if (!Number.isFinite(requestedScore)) {
      throw buildHttpError('Score harus berupa angka 0 sampai 100.');
    }

    const nextScore = clamp(Math.round(requestedScore), 0, 100);

    await db.sequelize.transaction(async (transaction) => {
      const state = await this.ensureState(currentUser, transaction);
      const current = this.normalizeState(state);
      const previousScore = current.profile.trustScore;

      await state.update(
        {
          profile: {
            ...current.profile,
            trustScore: nextScore,
            verified: nextScore >= 80,
          },
          ledger: this.addLedgerItem(current.ledger, {
            title: 'Nexus Trust Score diperbarui',
            detail: `Score berubah dari ${previousScore} ke ${nextScore}.`,
            tone: nextScore >= previousScore ? 'success' : 'warning',
          }),
          updatedById: currentUser?.id || null,
        },
        { transaction },
      );
    });

    return this.getState(currentUser);
  }

  static async runModule(currentUser, moduleId) {
    await db.sequelize.transaction(async (transaction) => {
      const state = await this.ensureState(currentUser, transaction);
      const current = this.normalizeState(state);
      const target = current.modules.find((item) => item.id === moduleId);

      if (!target) {
        throw buildHttpError('Module VORTA Universe tidak ditemukan.', 404);
      }

      const modules = current.modules.map((item) => (
        item.id === moduleId
          ? {
            ...item,
            launches: toNumber(item.launches) + 1,
            lastRun: new Date().toISOString(),
            status: item.status === 'Beta' ? 'Beta Aktif' : 'Online',
          }
          : item
      ));
      const nextScore = clamp(current.profile.trustScore + toNumber(target.trustImpact, 1), 0, 100);

      await state.update(
        {
          profile: {
            ...current.profile,
            trustScore: nextScore,
            verified: nextScore >= 80,
          },
          modules,
          ledger: this.addLedgerItem(current.ledger, {
            title: `${target.name} dijalankan`,
            detail: `Module ${target.name} aktif dan menambah ${toNumber(target.trustImpact, 1)} poin trust.`,
            tone: 'success',
          }),
          updatedById: currentUser?.id || null,
        },
        { transaction },
      );
    });

    return this.getState(currentUser);
  }

  static buildProduct(payload, productNumber) {
    const name = typeof payload.name === 'string' ? payload.name.trim() : '';

    if (!name) {
      throw buildHttpError('Nama produk wajib diisi.');
    }

    return {
      id: payload.id || `VU-PRD-${String(productNumber).padStart(3, '0')}`,
      name,
      price: clamp(Math.round(toNumber(payload.price, 0)), 0, 999999999),
      seller: payload.seller || 'VORTA Universe Seller',
      category: payload.category || 'General',
      stock: clamp(Math.round(toNumber(payload.stock, 1)), 0, 999999),
    };
  }

  static async createProduct(currentUser, payload) {
    await db.sequelize.transaction(async (transaction) => {
      const state = await this.ensureState(currentUser, transaction);
      const current = this.normalizeState(state);
      const product = this.buildProduct(payload, current.products.length + 1);

      await state.update(
        {
          products: [product, ...current.products].slice(0, 20),
          ledger: this.addLedgerItem(current.ledger, {
            title: 'Produk marketplace ditambahkan',
            detail: `${product.name} dari ${product.seller} tersimpan di VORTA Universe.`,
            tone: 'success',
          }),
          updatedById: currentUser?.id || null,
        },
        { transaction },
      );
    });

    return this.getState(currentUser);
  }


  static async addStateLedger(currentUser, item, transaction) {
    const state = await this.ensureState(currentUser, transaction);
    const current = this.normalizeState(state);

    await state.update(
      {
        ledger: this.addLedgerItem(current.ledger, item),
        updatedById: currentUser?.id || null,
      },
      { transaction },
    );
  }

  static serializeComment(comment) {
    const row = comment.get ? comment.get({ plain: true }) : comment;

    return {
      id: row.id,
      post_id: row.post_id,
      user: row.user,
      comment: row.comment,
      parent_id: row.parent_id || '0',
      created_at: row.created_at,
    };
  }

  static serializePost(post, comments) {
    const row = post.get ? post.get({ plain: true }) : post;

    return {
      id: row.id,
      user: row.user,
      content: row.content,
      created_at: row.created_at,
      comments,
    };
  }

  static async ensureDefaultSocialFeed(currentUser, transaction) {
    const where = getScopedWhere(currentUser);
    const existingPost = await db.vorta_social_posts.findOne({ where, transaction });

    if (existingPost) {
      return;
    }

    const post = await db.vorta_social_posts.create(
      {
        ...where,
        user: 'VORTA System',
        content: 'Selamat datang di feed VORTA Universe. Ini versi PostgreSQL dari prototype posts SQLite.',
        importHash: makeImportHash('social-welcome-post', currentUser),
      },
      { transaction },
    );

    await db.vorta_social_comments.create(
      {
        ...where,
        post_id: post.id,
        user: getDisplayName(currentUser),
        comment: 'Feed aktif: post, komentar, dan reply siap tersimpan permanen.',
        parent_id: '0',
        importHash: makeImportHash('social-welcome-comment', currentUser),
      },
      { transaction },
    );
  }

  static async buildSocialFeed(currentUser, transaction) {
    const where = getScopedWhere(currentUser);
    const posts = await db.vorta_social_posts.findAll({
      where,
      order: [['created_at', 'DESC']],
      limit: 20,
      transaction,
    });
    const postIds = posts.map((post) => post.id);
    const comments = postIds.length
      ? await db.vorta_social_comments.findAll({
        where: {
          ...where,
          post_id: {
            [db.Sequelize.Op.in]: postIds,
          },
        },
        order: [['created_at', 'ASC']],
        transaction,
      })
      : [];

    const commentsByPost = comments.reduce((acc, comment) => {
      const serialized = this.serializeComment(comment);
      acc[serialized.post_id] = acc[serialized.post_id] || [];
      acc[serialized.post_id].push(serialized);
      return acc;
    }, {});

    return {
      posts: posts.map((post) => this.serializePost(post, commentsByPost[post.id] || [])),
      stats: {
        postsCount: posts.length,
        commentsCount: comments.length,
      },
      syncedAt: new Date().toISOString(),
    };
  }

  static async getSocialFeed(currentUser) {
    return db.sequelize.transaction(async (transaction) => {
      await this.ensureDefaultSocialFeed(currentUser, transaction);
      return this.buildSocialFeed(currentUser, transaction);
    });
  }

  static async resetSocialFeed(currentUser) {
    await db.sequelize.transaction(async (transaction) => {
      const where = getScopedWhere(currentUser);

      await db.vorta_social_comments.destroy({ where, transaction });
      await db.vorta_social_posts.destroy({ where, transaction });
      await this.ensureDefaultSocialFeed(currentUser, transaction);
      await this.addStateLedger(currentUser, {
        title: 'Social feed VORTA di-reset',
        detail: 'Posts dan comments scoped user ini dikembalikan ke feed demo awal.',
        tone: 'warning',
      }, transaction);
    });

    return {
      feed: await this.getSocialFeed(currentUser),
      state: await this.getState(currentUser),
    };
  }

  static async createSocialPost(currentUser, payload) {
    const content = typeof payload.content === 'string' ? payload.content.trim() : '';

    if (!content) {
      throw buildHttpError('Konten post wajib diisi.');
    }

    await db.sequelize.transaction(async (transaction) => {
      await this.ensureDefaultSocialFeed(currentUser, transaction);
      const where = getScopedWhere(currentUser);

      await db.vorta_social_posts.create(
        {
          ...where,
          user: payload.user || getDisplayName(currentUser),
          content,
        },
        { transaction },
      );

      await this.addStateLedger(currentUser, {
        title: 'Post komunitas dibuat',
        detail: 'Konten baru tersimpan di tabel vorta_social_posts PostgreSQL.',
        tone: 'success',
      }, transaction);
    });

    return {
      feed: await this.getSocialFeed(currentUser),
      state: await this.getState(currentUser),
    };
  }

  static async createSocialComment(currentUser, postId, payload) {
    const commentText = typeof payload.comment === 'string' ? payload.comment.trim() : '';

    if (!commentText) {
      throw buildHttpError('Komentar wajib diisi.');
    }

    await db.sequelize.transaction(async (transaction) => {
      const where = getScopedWhere(currentUser);
      const post = await db.vorta_social_posts.findOne({
        where: {
          ...where,
          id: postId,
        },
        transaction,
      });

      if (!post) {
        throw buildHttpError('Post VORTA tidak ditemukan.', 404);
      }

      await db.vorta_social_comments.create(
        {
          ...where,
          post_id: post.id,
          user: payload.user || getDisplayName(currentUser),
          comment: commentText,
          parent_id: payload.parent_id || '0',
        },
        { transaction },
      );

      await this.addStateLedger(currentUser, {
        title: 'Komentar komunitas dibuat',
        detail: 'Komentar tersimpan di tabel vorta_social_comments PostgreSQL.',
        tone: 'info',
      }, transaction);
    });

    return {
      feed: await this.getSocialFeed(currentUser),
      state: await this.getState(currentUser),
    };
  }

  static async requestOtp(currentUser, payload) {
    const phone = normalizePhone(payload.phone);

    if (!phone || phone.length < 8) {
      throw buildHttpError('Nomor HP wajib diisi minimal 8 digit.');
    }

    const otp = makeOtp();
    const otpHash = await bcrypt.hash(otp, 10);
    const expiresAt = new Date(Date.now() + 5 * 60 * 1000);

    await db.sequelize.transaction(async (transaction) => {
      const where = getScopedWhere(currentUser);

      await db.vorta_login_otps.update(
        {
          status: 'superseded',
          updatedById: currentUser?.id || null,
        },
        {
          where: {
            ...where,
            phone,
            status: 'pending',
          },
          transaction,
        },
      );

      await db.vorta_login_otps.create(
        {
          ...where,
          phone,
          otpHash,
          expiresAt,
          importHash: `vorta-otp-${Date.now()}-${Math.random().toString(16).slice(2)}`,
          createdById: currentUser?.id || null,
          updatedById: currentUser?.id || null,
        },
        { transaction },
      );

      await this.addStateLedger(currentUser, {
        title: 'OTP development dibuat',
        detail: `Kode OTP untuk ${phone} berlaku 5 menit dan tersimpan sebagai hash.`,
        tone: 'info',
      }, transaction);
    });

    return {
      message: 'OTP dev berhasil dibuat. Untuk produksi, sambungkan endpoint ini ke SMS/WhatsApp provider.',
      phone,
      devOtp: otp,
      expiresAt,
      state: await this.getState(currentUser),
    };
  }

  static async verifyOtp(currentUser, payload) {
    const phone = normalizePhone(payload.phone);
    const otp = String(payload.otp || '').trim();

    if (!phone || !otp) {
      throw buildHttpError('Nomor HP dan OTP wajib diisi.');
    }

    await db.sequelize.transaction(async (transaction) => {
      const where = getScopedWhere(currentUser);
      const record = await db.vorta_login_otps.findOne({
        where: {
          ...where,
          phone,
          status: 'pending',
          expiresAt: {
            [db.Sequelize.Op.gt]: new Date(),
          },
        },
        order: [['createdAt', 'DESC']],
        transaction,
      });

      if (!record) {
        throw buildHttpError('OTP tidak ditemukan atau sudah kedaluwarsa.', 400);
      }

      const isValid = await bcrypt.compare(otp, record.otpHash);
      const attempts = toNumber(record.attempts, 0) + 1;

      if (!isValid) {
        await record.update(
          {
            attempts,
            status: attempts >= 5 ? 'locked' : 'pending',
            updatedById: currentUser?.id || null,
          },
          { transaction },
        );

        throw buildHttpError('OTP salah. Silakan cek kode dan coba lagi.', 400);
      }

      await record.update(
        {
          attempts,
          status: 'verified',
          verifiedAt: new Date(),
          updatedById: currentUser?.id || null,
        },
        { transaction },
      );

      const state = await this.ensureState(currentUser, transaction);
      const current = this.normalizeState(state);
      const nextScore = clamp(current.profile.trustScore + 5, 0, 100);

      await state.update(
        {
          profile: {
            ...current.profile,
            phone,
            phoneVerified: true,
            trustScore: nextScore,
            verified: nextScore >= 80,
          },
          ledger: this.addLedgerItem(current.ledger, {
            title: 'OTP HP terverifikasi',
            detail: `${phone} lolos verifikasi OTP dan menambah 5 poin trust.`,
            tone: 'success',
          }),
          updatedById: currentUser?.id || null,
        },
        { transaction },
      );
    });

    return {
      message: 'OTP valid. Nomor HP terverifikasi dan Trust Score naik +5.',
      verified: true,
      state: await this.getState(currentUser),
    };
  }

  static async createDemoProduct(currentUser) {
    const productIndex = Date.now().toString().slice(-4);

    return this.createProduct(currentUser, {
      id: `VU-PRD-${productIndex}`,
      name: `Produk Demo Universe ${productIndex}`,
      price: 125000 + (Number(productIndex) % 5) * 25000,
      seller: 'VORTA Demo Seller',
      category: 'Demo Product',
      stock: 24 + (Number(productIndex) % 20),
    });
  }
}

module.exports = VortaUniverseService;
