/* ============================================================
   db.js — IndexedDB persistence layer for Tamasa
   ------------------------------------------------------------
   CRM-ready schema. The `contacts` store holds denormalized
   contact documents. Indexes enable fast search/filter/sort.
   A `tags` store holds tag definitions (for colors & metadata).
   A `settings` store holds app-level key/value config.

   Future CRM expansion can add stores: campaigns, templates,
   interactions (calls/emails/notes timeline), deals, etc.
   ============================================================ */
(function (global) {
  'use strict';

  var DB_NAME = 'tamasa';
  var DB_VERSION = 1;

  var db = null;

  /* ---- Low-level open ---- */
  function open() {
    return new Promise(function (resolve, reject) {
      if (db) return resolve(db);
      var req = indexedDB.open(DB_NAME, DB_VERSION);

      req.onupgradeneeded = function (e) {
        var idb = e.target.result;

        if (!idb.objectStoreNames.contains('contacts')) {
          var contacts = idb.createObjectStore('contacts', { keyPath: 'id' });
          // Efficient search/filter/sort indexes
          contacts.createIndex('lastName', 'lastName', { unique: false });
          contacts.createIndex('firstName', 'firstName', { unique: false });
          contacts.createIndex('company', 'company', { unique: false });
          contacts.createIndex('starred', 'starred', { unique: false });
          contacts.createIndex('createdAt', 'createdAt', { unique: false });
          contacts.createIndex('updatedAt', 'updatedAt', { unique: false });
          // Multi-entry index for tags -> fast "contacts by tag" queries
          contacts.createIndex('tags', 'tags', { unique: false, multiEntry: true });
          // Derived "search blob" index for fast text lookup
          contacts.createIndex('searchKey', 'searchKey', { unique: false });
        }

        if (!idb.objectStoreNames.contains('tags')) {
          idb.createObjectStore('tags', { keyPath: 'id' });
        }

        if (!idb.objectStoreNames.contains('settings')) {
          idb.createObjectStore('settings', { keyPath: 'key' });
        }

        // Future CRM stores (reserved, created when needed):
        // - campaigns  { id, name, type, audience[], ... }
        // - templates  { id, name, type, subject, body, ... }
        // - interactions { id, contactId, type, datetime, ... }
      };

      req.onsuccess = function (e) { db = e.target.result; resolve(db); };
      req.onerror = function (e) { reject(e.target.error); };
    });
  }

  /* ---- Generic helpers ---- */
  function tx(store, mode) {
    return db.transaction(store, mode).objectStore(store);
  }

  function reqToPromise(request) {
    return new Promise(function (resolve, reject) {
      request.onsuccess = function () { resolve(request.result); };
      request.onerror = function () { reject(request.error); };
    });
  }

  /* =========================================================
     CONTACTS
     ========================================================= */

  // Build derived fields used for searching & sorting
  function buildDerived(contact) {
    var nameParts = [];
    if (contact.firstName) nameParts.push(contact.firstName);
    if (contact.lastName) nameParts.push(contact.lastName);
    contact.fullName = nameParts.join(' ').trim();

    // Normalize for search: lowercase, strip Persian/Arabic diacritics,
    // unify Arabic Yeh/Kaf to Persian, strip spaces -> resilient matching.
    var blob = [
      contact.fullName,
      contact.company || '',
      contact.jobTitle || '',
      contact.email || '',
      (contact.emails || []).join(' '),
      (contact.phones || []).map(function (p) { return p && p.value ? p.value : ''; }).join(' '),
      contact.address || '',
      contact.notes || '',
      (contact.tags || []).join(' ')
    ].join(' ');

    contact.searchKey = normalizeSearch(blob);
    return contact;
  }

  function normalizeSearch(text) {
    if (!text) return '';
    return String(text)
      // unify Arabic letters to Persian
      .replace(/\u064A/g, '\u06CC') // ي -> ی
      .replace(/\u0643/g, '\u06A9') // ك -> ک
      .replace(/\u0649/g, '\u06CC') // ٯ -> ی
      .replace(/\u0623|\u0625|\u0622/g, '\u0627') // أإآ -> ا
      .replace(/\u0647\u200C/g, '\u0647') // heh + ZWNJ
      .replace(/\u200C/g, ' ')
      // unify digits to Persian/Latin lowercase
      .replace(/[۰-۹]/g, function (d) { return '۰۱۲۳۴۵۶۷۸۹'.indexOf(d); })
      .replace(/[٠-٩]/g, function (d) { return '٠١٢٣٤٥٦٧٨٩'.indexOf(d); })
      .toLowerCase()
      .replace(/[\s\-()._]+/g, ' ')
      .trim();
  }

  // Normalize a user search term the same way (also strip leading zeros in phones)
  function normalizeQuery(term) {
    return normalizeSearch(term);
  }

  var Contacts = {
    // returns id of saved contact
    save: function (contact) {
      buildDerived(contact);
      if (!contact.id) contact.id = uid();
      var now = Date.now();
      if (!contact.createdAt) contact.createdAt = now;
      contact.updatedAt = now;
      return reqToPromise(tx('contacts', 'readwrite').put(contact)).then(function () {
        return contact;
      });
    },

    get: function (id) {
      return reqToPromise(tx('contacts', 'readonly').get(id));
    },

    getAll: function () {
      return reqToPromise(tx('contacts', 'readonly').getAll());
    },

    remove: function (id) {
      return reqToPromise(tx('contacts', 'readwrite').delete(id));
    },

    // Bulk save (for import) — atomic in a single tx
    saveMany: function (contacts) {
      return new Promise(function (resolve, reject) {
        var t = db.transaction('contacts', 'readwrite');
        var store = t.objectStore('contacts');
        var count = 0;
        contacts.forEach(function (c) {
          buildDerived(c);
          if (!c.id) c.id = uid();
          if (!c.createdAt) c.createdAt = Date.now();
          c.updatedAt = Date.now();
          store.put(c);
          count++;
        });
        t.oncomplete = function () { resolve(count); };
        t.onerror = function () { reject(t.error); };
      });
    },

    // Replace entire dataset (for restore) — atomic wipe + bulk insert
    replaceAll: function (contacts) {
      return new Promise(function (resolve, reject) {
        var t = db.transaction('contacts', 'readwrite');
        var store = t.objectStore('contacts');
        store.clear();
        contacts.forEach(function (c) {
          buildDerived(c);
          if (!c.id) c.id = uid();
          if (!c.createdAt) c.createdAt = Date.now();
          c.updatedAt = Date.now();
          store.put(c);
        });
        t.oncomplete = function () { resolve(contacts.length); };
        t.onerror = function () { reject(t.error); };
      });
    },

    clear: function () {
      return reqToPromise(tx('contacts', 'readwrite').clear());
    }
  };

  /* =========================================================
     TAGS
     ========================================================= */
  var TAG_COLORS = ['#6366f1', '#ec4899', '#14b8a6', '#f59e0b', '#8b5cf6', '#ef4444', '#0ea5e9', '#22c55e'];
  function colorForTag(name) {
    var h = 0;
    for (var i = 0; i < name.length; i++) h = (h * 31 + name.charCodeAt(i)) >>> 0;
    return TAG_COLORS[h % TAG_COLORS.length];
  }

  var Tags = {
    getAll: function () { return reqToPromise(tx('tags', 'readonly').getAll()); },
    // Ensure a tag exists; create if missing
    ensure: function (name) {
      var id = name.trim().toLowerCase();
      if (!id) return Promise.resolve(null);
      return reqToPromise(tx('tags', 'readonly').get(id)).then(function (existing) {
        if (existing) return existing;
        var tag = {
          id: id,
          name: name.trim(),
          color: colorForTag(name),
          createdAt: Date.now()
        };
        return reqToPromise(tx('tags', 'readwrite').put(tag)).then(function () { return tag; });
      });
    },
    remove: function (id) { return reqToPromise(tx('tags', 'readwrite').delete(id)); },
    colorFor: colorForTag
  };

  /* =========================================================
     SETTINGS
     ========================================================= */
  var Settings = {
    get: function (key) {
      return reqToPromise(tx('settings', 'readonly').get(key)).then(function (r) { return r ? r.value : null; });
    },
    set: function (key, value) {
      return reqToPromise(tx('settings', 'readwrite').put({ key: key, value: value }));
    }
  };

  /* =========================================================
     UTIL
     ========================================================= */
  function uid() {
    if (global.crypto && global.crypto.randomUUID) return global.crypto.randomUUID();
    return 'c_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 10);
  }

  /* =========================================================
     PUBLIC API
     ========================================================= */
  global.DB = {
    open: open,
    Contacts: Contacts,
    Tags: Tags,
    Settings: Settings,
    normalizeQuery: normalizeQuery,
    normalizeSearch: normalizeSearch,
    uid: uid
  };

})(window);
