'use strict';

const express = require('express');

const BUILDINGS_CACHE_TTL_MS = 30 * 1000; // 30 секунд

function createResponseCache() {
  const store = new Map();
  return {
    get(key) {
      const entry = store.get(key);
      if (!entry || Date.now() > entry.expiresAt) {
        if (entry) store.delete(key);
        return null;
      }
      return entry.data;
    },
    set(key, data, ttlMs = BUILDINGS_CACHE_TTL_MS) {
      store.set(key, { data, expiresAt: Date.now() + ttlMs });
    },
    invalidatePrefix(prefix) {
      for (const key of store.keys()) {
        if (key.startsWith(prefix)) store.delete(key);
      }
    }
  };
}

/**
 * Buildings API router.
 * @param {{ query: (text: string, params?: any[]) => Promise<any[]> }} deps
 * @returns {express.Router}
 */
function createBuildingsRouter(deps) {
  const { query } = deps;
  const router = express.Router();
  const cache = createResponseCache();

  function invalidateBuildingsCache() {
    cache.invalidatePrefix('buildings:');
  }

  // GET /buildings -> список домов (пагинация, опционально ?light=1). При scope=own_district — дома назначенных участков (employee_districts + fallback assigned_district_id).
  router.get('/buildings', async (req, res) => {
    try {
      const limit = Math.min(Math.max(1, parseInt(req.query.limit, 10) || 100), 500);
      const offset = Math.max(0, parseInt(req.query.offset, 10) || 0);
      const light = req.query.light === '1' || req.query.light === 'true';

      let districtIds = null; // null = все участки, [] = нет доступа, [id, ...] = только эти участки
      if (req.user && req.user.userId) {
        const userScopeRows = await query(
          `SELECT pu.scope, e.id AS employee_id, e.assigned_district_id
           FROM portal_users pu
           JOIN employees e ON e.id = pu.employee_id
           WHERE pu.id = $1`,
          [req.user.userId]
        );
        const row = userScopeRows[0];
        if (row && row.scope === 'own_district') {
          let ids = [];
          try {
            const edRows = await query(
              'SELECT district_id FROM employee_districts WHERE employee_id = $1 ORDER BY district_id',
              [row.employee_id]
            );
            ids = (edRows || []).map((r) => r.district_id).filter(Boolean);
          } catch (_) {}
          if (ids.length === 0 && row.assigned_district_id) {
            ids = [row.assigned_district_id];
          }
          districtIds = ids;
        }
      }

      const cacheKey = districtIds === null
        ? `buildings:list:${limit}:${offset}:${light}`
        : `buildings:list:${limit}:${offset}:${light}:districts:${[...districtIds].sort().join(',')}`;

      const cached = districtIds === null ? cache.get(cacheKey) : null;
      if (cached !== null) {
        return res.json(cached);
      }

      let rows;
      if (districtIds !== null && districtIds.length === 0) {
        rows = [];
      } else if (districtIds !== null && districtIds.length > 0) {
        rows = await query(
          `SELECT id, data FROM buildings WHERE (data->>'districtId') = ANY($1::text[]) ORDER BY id LIMIT $2 OFFSET $3`,
          [districtIds, limit, offset]
        );
      } else {
        rows = await query(
          'SELECT id, data FROM buildings ORDER BY id LIMIT $1 OFFSET $2',
          [limit, offset]
        );
      }

      let data;
      if (light) {
        data = rows.map((r) => {
          const d = r.data || {};
          return {
            id: d.id || r.id,
            districtId: d.districtId ?? null,
            passport: d.passport ? { address: d.passport.address || '' } : { address: '' }
          };
        });
      } else {
        data = rows.map((r) => r.data);
        const ids = rows.map((r) => (r.data && r.data.id) || r.id).filter(Boolean);
        if (ids.length > 0) {
          try {
            const placeholders = ids.map((_, i) => `$${i + 1}`).join(', ');
            const accountRows = await query(
              `SELECT building_id, data FROM building_personal_accounts WHERE building_id IN (${placeholders})`,
              ids
            );
            const accountsByBuilding = {};
            for (const ar of accountRows) {
              if (!accountsByBuilding[ar.building_id]) accountsByBuilding[ar.building_id] = [];
              accountsByBuilding[ar.building_id].push(ar.data);
            }
            for (const b of data) {
              const bid = b.id;
              b.accounts = Array.isArray(accountsByBuilding[bid]) ? accountsByBuilding[bid] : (Array.isArray(b.accounts) ? b.accounts : []);
            }
          } catch (e) {
            if (!e.message || !e.message.includes('building_personal_accounts')) {
              console.warn('Error loading accounts for list:', e.message);
            }
          }
        }
      }
      if (districtIds === null) cache.set(cacheKey, data);
      res.json(data);
    } catch (err) {
      console.error('Error fetching buildings:', err);
      res.status(500).json({ error: 'Failed to fetch buildings' });
    }
  });

  // POST /buildings -> создание нового дома
  router.post('/buildings', async (req, res) => {
    const building = req.body;

    if (!building || !building.id || !building.passport || !building.passport.address) {
      return res
        .status(400)
        .json({ error: 'Ожидается объект Building с полями id и passport.address' });
    }

    try {
      await query('INSERT INTO buildings (id, data) VALUES ($1, $2)', [
        building.id,
        building,
      ]);

      try {
        const existingSurvey = await query(
          `SELECT id FROM nps_surveys WHERE building_id = $1`,
          [building.id]
        );
        if (existingSurvey.length === 0) {
          const accessKey = `nps-${building.id}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
          await query(
            `INSERT INTO nps_surveys 
             (building_id, title, description, status, access_key, created_by)
             VALUES ($1, $2, $3, $4, $5, 'system')`,
            [
              building.id,
              'Опрос удовлетворенности жителей',
              'Помогите нам стать лучше! Поделитесь своим мнением о качестве обслуживания.',
              'draft',
              accessKey
            ]
          );
          console.log(`[Auto NPS] Создан опрос для нового дома ${building.id}`);
        }
      } catch (npsErr) {
        console.error(`[Auto NPS] Ошибка создания опроса для дома ${building.id}:`, npsErr);
      }

      try {
        const now = new Date();
        const currentMonthStart = new Date(now.getFullYear(), now.getMonth(), 1);
        const currentMonthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59);
        const months = [
          'Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь',
          'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'
        ];
        const monthName = months[now.getMonth()];
        const reportMonth = `${monthName} ${now.getFullYear()}`;
        const existingReport = await query(
          `SELECT id FROM resident_reports 
           WHERE building_id = $1 AND month = $2`,
          [building.id, reportMonth]
        );
        if (existingReport.length === 0) {
          const initialContent = {
            applications: { total: 156, completed: 153, quality: 98 },
            finances: { collected: 2400000, expenses: 1890000, balance: 560000 },
            nps: { score: 72, totalResponses: 45 }
          };
          const newReport = await query(
            `INSERT INTO resident_reports 
             (building_id, month, period_start, period_end, status, content, published_at)
             VALUES ($1, $2, $3, $4, $5, $6::jsonb, NOW())
             RETURNING id`,
            [
              building.id,
              reportMonth,
              currentMonthStart.toISOString().split('T')[0],
              currentMonthEnd.toISOString().split('T')[0],
              'published',
              JSON.stringify(initialContent)
            ]
          );
          console.log(`[Auto Report] Создан опубликованный отчет для нового дома ${building.id} (ID: ${newReport[0].id})`);
        }
      } catch (reportErr) {
        console.error(`[Auto Report] Ошибка создания отчета для дома ${building.id}:`, reportErr);
      }

      invalidateBuildingsCache();
      res.status(201).json(building);
    } catch (err) {
      console.error('Error creating building:', err);
      res.status(500).json({ error: 'Failed to create building' });
    }
  });

  // GET /buildings/:id — здание + лицевые счета (из building_personal_accounts или из data)
  router.get('/buildings/:id', async (req, res) => {
    try {
      const { id } = req.params;
      const cacheKey = `buildings:id:${id}`;
      const cached = cache.get(cacheKey);
      if (cached !== null) {
        return res.json(cached);
      }
      const rows = await query('SELECT data FROM buildings WHERE id = $1', [id]);
      if (!rows.length) {
        return res.status(404).json({ error: 'Дом не найден' });
      }
      const data = rows[0].data;
      try {
        const accountRows = await query(
          'SELECT id, data FROM building_personal_accounts WHERE building_id = $1',
          [id]
        );
        if (accountRows.length > 0) {
          data.accounts = accountRows.map((r) => r.data);
        } else if (!Array.isArray(data.accounts)) {
          data.accounts = [];
        }
      } catch (e) {
        if (!Array.isArray(data.accounts)) data.accounts = [];
      }
      cache.set(cacheKey, data);
      res.json(data);
    } catch (err) {
      console.error('Error fetching building:', err);
      res.status(500).json({ error: err.message || 'Failed to fetch building' });
    }
  });

  // PUT /buildings/:id — если есть таблица building_personal_accounts: не пишем accounts в data; иначе пишем всё (fallback)
  router.put('/buildings/:id', async (req, res) => {
    const { id } = req.params;
    const building = req.body;
    if (!building || !building.passport || !building.passport.address) {
      return res
        .status(400)
        .json({ error: 'Ожидается объект Building с полями passport.address' });
    }
    try {
      let useAccountsTable = false;
      try {
        await query('SELECT 1 FROM building_personal_accounts LIMIT 1');
        useAccountsTable = true;
      } catch (_) {}
      const toSave = { ...building };
      if (useAccountsTable) delete toSave.accounts;
      await query('UPDATE buildings SET data = $2 WHERE id = $1', [id, toSave]);
      invalidateBuildingsCache();
      const dataForResponse = { ...toSave };
      if (useAccountsTable) {
        try {
          const accountRows = await query(
            'SELECT id, data FROM building_personal_accounts WHERE building_id = $1',
            [id]
          );
          dataForResponse.accounts = accountRows.length > 0 ? accountRows.map((r) => r.data) : [];
        } catch (e) {
          dataForResponse.accounts = Array.isArray(building.accounts) ? building.accounts : [];
        }
      } else {
        dataForResponse.accounts = Array.isArray(building.accounts) ? building.accounts : [];
      }
      res.json(dataForResponse);
    } catch (err) {
      console.error('Error updating building:', err);
      res.status(500).json({ error: 'Failed to update building' });
    }
  });

  // DELETE /buildings/:id
  router.delete('/buildings/:id', async (req, res) => {
    const { id } = req.params;
    try {
      const buildingRows = await query('SELECT id FROM buildings WHERE id = $1', [id]);
      if (buildingRows.length === 0) {
        return res.status(404).json({ error: 'Дом не найден' });
      }
      await query('DELETE FROM buildings WHERE id = $1', [id]);
      invalidateBuildingsCache();
      res.json({ success: true, message: 'Дом успешно удален' });
    } catch (err) {
      console.error('Error deleting building:', err);
      res.status(500).json({ error: 'Ошибка при удалении дома' });
    }
  });

  // POST /buildings/:id/accounts — в building_personal_accounts или в buildings.data (fallback)
  router.post('/buildings/:id/accounts', async (req, res) => {
    const { id: buildingId } = req.params;
    const newAccount = req.body;
    if (!newAccount || !newAccount.apartmentNumber) {
      return res.status(400).json({ error: 'Ожидается объект PersonalAccount с полем apartmentNumber' });
    }
    try {
      const buildingRows = await query('SELECT data FROM buildings WHERE id = $1', [buildingId]);
      if (buildingRows.length === 0) {
        return res.status(404).json({ error: 'Building not found' });
      }
      let useAccountsTable = false;
      try {
        await query('SELECT 1 FROM building_personal_accounts LIMIT 1');
        useAccountsTable = true;
      } catch (_) {}
      const accounts = Array.isArray(buildingRows[0].data?.accounts) ? buildingRows[0].data.accounts : [];
      if (!newAccount.id) newAccount.id = `${buildingId}-acc-${Date.now()}`;
      if (!newAccount.accountNumber) newAccount.accountNumber = `${buildingId.replace('b-', '')}00${accounts.length + 1}`;
      if (useAccountsTable) {
        await query(
          'INSERT INTO building_personal_accounts (id, building_id, data) VALUES ($1, $2, $3)',
          [newAccount.id, buildingId, newAccount]
        );
      } else {
        const building = buildingRows[0].data;
        building.accounts = [...accounts, newAccount];
        building.isDirty = true;
        await query('UPDATE buildings SET data = $2 WHERE id = $1', [buildingId, building]);
      }
      invalidateBuildingsCache();
      res.status(201).json(newAccount);
    } catch (err) {
      console.error('Error creating account:', err);
      res.status(500).json({ error: 'Failed to create account' });
    }
  });

  // PUT /buildings/:id/accounts/:accountId — обновление в building_personal_accounts или в data (fallback)
  router.put('/buildings/:id/accounts/:accountId', async (req, res) => {
    const { id: buildingId, accountId } = req.params;
    const updatedAccount = req.body;
    if (!updatedAccount || !updatedAccount.id) {
      return res.status(400).json({ error: 'Ожидается объект PersonalAccount с полем id' });
    }
    try {
      const buildingRows = await query('SELECT data FROM buildings WHERE id = $1', [buildingId]);
      if (buildingRows.length === 0) {
        return res.status(404).json({ error: 'Building not found' });
      }
      let useAccountsTable = false;
      try {
        await query('SELECT 1 FROM building_personal_accounts LIMIT 1');
        useAccountsTable = true;
      } catch (_) {}
      if (useAccountsTable) {
        const updateResult = await query(
          'UPDATE building_personal_accounts SET data = $3 WHERE id = $1 AND building_id = $2 RETURNING id',
          [accountId, buildingId, updatedAccount]
        );
        if (updateResult.length === 0) {
          return res.status(404).json({ error: 'Account not found' });
        }
      } else {
        const building = buildingRows[0].data;
        const accounts = Array.isArray(building.accounts) ? building.accounts : [];
        const idx = accounts.findIndex((a) => a.id === accountId);
        if (idx === -1) return res.status(404).json({ error: 'Account not found' });
        building.accounts = accounts.map((a) => (a.id === accountId ? updatedAccount : a));
        building.isDirty = true;
        await query('UPDATE buildings SET data = $2 WHERE id = $1', [buildingId, building]);
      }
      invalidateBuildingsCache();
      res.json(updatedAccount);
    } catch (err) {
      console.error('Error updating account:', err);
      res.status(500).json({ error: 'Failed to update account' });
    }
  });

  // DELETE /buildings/:id/accounts/:accountId — из building_personal_accounts или из data (fallback)
  router.delete('/buildings/:id/accounts/:accountId', async (req, res) => {
    const { id: buildingId, accountId } = req.params;
    try {
      const buildingRows = await query('SELECT data FROM buildings WHERE id = $1', [buildingId]);
      if (buildingRows.length === 0) {
        return res.status(404).json({ error: 'Building not found' });
      }
      let useAccountsTable = false;
      try {
        await query('SELECT 1 FROM building_personal_accounts LIMIT 1');
        useAccountsTable = true;
      } catch (_) {}
      if (useAccountsTable) {
        const deleteResult = await query(
          'DELETE FROM building_personal_accounts WHERE id = $1 AND building_id = $2 RETURNING id',
          [accountId, buildingId]
        );
        if (deleteResult.length === 0) {
          return res.status(404).json({ error: 'Account not found' });
        }
      } else {
        const building = buildingRows[0].data;
        const accounts = Array.isArray(building.accounts) ? building.accounts : [];
        const filtered = accounts.filter((a) => a.id !== accountId);
        if (filtered.length === accounts.length) {
          return res.status(404).json({ error: 'Account not found' });
        }
        building.accounts = filtered;
        building.isDirty = true;
        await query('UPDATE buildings SET data = $2 WHERE id = $1', [buildingId, building]);
      }
      invalidateBuildingsCache();
      res.json({ success: true, message: 'Account deleted' });
    } catch (err) {
      console.error('Error deleting account:', err);
      res.status(500).json({ error: 'Failed to delete account' });
    }
  });

  return router;
}

module.exports = createBuildingsRouter;
