// SSPHONE data store — seed data + reducer + context
// Persists to localStorage key "ssphone-v3"

const DAY = 86400000;
const now = Date.now();

const BRANDS = ["Apple", "Samsung", "Xiaomi", "OPPO", "Vivo", "Realme", "Huawei", "Honor", "OnePlus", "Infinix", "Tecno", "Nokia", "OEM", "อื่นๆ"];

const BRANCHES = [
  { id: "B1", name: "สาขาสีลม", short: "สีลม", tel: "02-234-5678", addr: "112/4 ถ.สีลม แขวงสุริยวงศ์ เขตบางรัก กทม. 10500", taxId: "0105561023457" },
  { id: "B2", name: "สาขาลาดพร้าว", short: "ลาดพร้าว", tel: "02-512-3344", addr: "555 ถ.ลาดพร้าว แขวงจอมพล เขตจตุจักร กทม. 10900", taxId: "0105561023457" },
  { id: "B3", name: "สาขาระยอง", short: "ระยอง", tel: "038-861-220", addr: "99/12 ถ.สุขุมวิท ต.เนินพระ อ.เมือง ระยอง 21000", taxId: "0105561023457" },
];

const LOC_TYPES = [
  { code: "SHOP", name: "หน้าร้าน" }, { code: "SHOW", name: "ตู้โชว์" },
  { code: "BACK", name: "คลังหลังร้าน" }, { code: "LAB", name: "ห้องซ่อม" },
];
const LOCATIONS = [];
BRANCHES.forEach((b, bi) => LOC_TYPES.forEach((lt, li) => LOCATIONS.push({
  id: `${b.id}-${lt.code}`, branch: b.id, name: lt.name, code: `${String.fromCharCode(65 + bi)}${li + 1}`,
})));

// หมวดหมู่ (เพิ่ม/แก้ได้เอง) — แต่ละหมวดมี prefix สำหรับรัน SKU และหมวดย่อย
const CATS = [
  { name: "เครื่อง", prefix: "PH", subs: ["Apple", "Samsung", "Xiaomi", "OPPO", "Vivo", "Realme"] },
  { name: "เครื่องมือสอง", prefix: "PH", subs: ["Apple", "Samsung", "Xiaomi", "OPPO", "Vivo", "Realme"] },
  { name: "Notebook/MacBook", prefix: "NB", subs: ["MacBook", "ASUS", "Acer", "Lenovo", "HP", "Dell", "MSI"] },
  { name: "อุปกรณ์", prefix: "AC", subs: ["หัวชาร์จ", "สายชาร์จ", "เคส", "ฟิล์ม", "หูฟัง", "Powerbank"] },
  { name: "อะไหล่", prefix: "SP", subs: ["จอ", "แบตเตอรี่", "ช่องชาร์จ", "กล้อง"], parts: true },
];

// เลขรันต่อ prefix — freed = เลขที่ถูกลบ รอนำกลับมาใช้ใหม่
const SKU_POOL = {
  PH: { next: 5, freed: [] },
  AC: { next: 7, freed: [] },
  SP: { next: 5, freed: [] },
};

const ROLES = {
  owner: { label: "เจ้าของร้าน", pages: ["dashboard", "income", "pos", "pos_wholesale", "invoices", "buyin", "pawns", "repairs", "stock", "parts", "locations", "reports", "branches", "staff", "settings"], allBranch: true, stockAccess: "all", partsAccess: "all", dashboardAccess: true },
  manager: { label: "ผู้จัดการสาขา", pages: ["dashboard", "income", "pos", "pos_wholesale", "invoices", "buyin", "pawns", "repairs", "stock", "parts", "locations", "reports", "staff"], allBranch: false, stockAccess: "all-no-parts", partsAccess: "own", dashboardAccess: true },
  sales: { label: "พนักงานขาย", pages: ["dashboard", "income", "pos", "pos_wholesale", "buyin", "pawns", "stock"], allBranch: false, stockAccess: "own", partsAccess: "none", dashboardAccess: false },
  tech: { label: "ช่างซ่อม", pages: ["dashboard", "repairs", "stock", "parts"], allBranch: false, stockAccess: "all", partsAccess: "all", dashboardAccess: false },
  stock: { label: "พนักงานสต็อก", pages: ["dashboard", "stock", "parts", "locations", "reports"], allBranch: false, stockAccess: "all", partsAccess: "all", dashboardAccess: false },
};

const USERS = [
  { id: "U1", name: "สมชาย ใจดี", role: "owner", branch: "B1", img: null, username: "owner", password: "S12345678" },
  { id: "U2", name: "วิภา ศรีสุข", role: "manager", branch: "B1", img: null, username: "vipha", password: "Vipha1234" },
  { id: "U3", name: "ปอนด์ พงษ์พา", role: "sales", branch: "B1", img: null, username: "pond", password: "Pond1234" },
  { id: "U4", name: "บอย เทคนิค", role: "tech", branch: "B1", img: null, username: "boy", password: "Boy12345" },
  { id: "U5", name: "นัท จัดของ", role: "stock", branch: "B2", img: null, username: "nat", password: "Nat12345" },
  { id: "U6", name: "เก่ง ซ่อมไว", role: "tech", branch: "B2", img: null, username: "keng", password: "Keng1234" },
];

// stock: { locId: qty }
const PRODUCTS = [
  { id: "P01", sku: "PH-APPLE-000001", barcode: "8850001000011", name: "iPhone 13 Pro 128GB (มือสอง เกรด A)", cat: "เครื่องมือสอง", sub: "Apple", brand: "Apple", price: 21900, cost: 18500, reorder: 1, img: null, stock: { "B1-SHOW": 2, "B2-SHOW": 1 } },
  { id: "P02", sku: "PH-APPLE-000002", barcode: "8850001000028", name: "iPhone 12 64GB (มือสอง เกรด B+)", cat: "เครื่องมือสอง", sub: "Apple", brand: "Apple", price: 13900, cost: 11200, reorder: 1, img: null, stock: { "B1-SHOW": 1, "B3-SHOW": 2 } },
  { id: "P03", sku: "PH-SAMSUNG-000003", barcode: "8850001000035", name: "Samsung Galaxy A55 5G (เครื่องใหม่)", cat: "เครื่อง", sub: "Samsung", brand: "Samsung", price: 13499, cost: 11900, reorder: 2, img: null, stock: { "B1-SHOW": 3, "B2-SHOW": 2, "B3-SHOW": 1 } },
  { id: "P04", sku: "PH-XIAOMI-000004", barcode: "8850001000042", name: "Redmi Note 13 8/256 (เครื่องใหม่)", cat: "เครื่อง", sub: "Xiaomi", brand: "Xiaomi", price: 7999, cost: 6850, reorder: 2, img: null, stock: { "B1-SHOW": 4, "B2-SHOW": 3 } },
  { id: "P05", sku: "AC-APPLE-000001", barcode: "8850002000010", name: "ฟิล์มกระจก iPhone 15 (ใสเต็มจอ)", cat: "อุปกรณ์", sub: "ฟิล์ม", brand: "Apple", price: 190, cost: 45, reorder: 10, img: null, stock: { "B1-SHOP": 3, "B2-SHOP": 14, "B3-SHOP": 8 } },
  { id: "P06", sku: "AC-SAMSUNG-000002", barcode: "8850002000027", name: "เคสใสกันกระแทก Galaxy S24", cat: "อุปกรณ์", sub: "เคส", brand: "Samsung", price: 290, cost: 90, reorder: 8, img: null, stock: { "B1-SHOP": 12, "B2-SHOP": 6 } },
  { id: "P07", sku: "AC-OEM-000003", barcode: "8850002000034", name: "สายชาร์จ USB-C to C 60W 1m", cat: "อุปกรณ์", sub: "สายชาร์จ", brand: "OEM", price: 259, cost: 95, reorder: 12, img: null, stock: { "B1-SHOP": 5, "B2-SHOP": 18, "B3-SHOP": 10 } },
  { id: "P08", sku: "AC-OEM-000004", barcode: "8850002000041", name: "หัวชาร์จ 20W PD (มอก.)", cat: "อุปกรณ์", sub: "หัวชาร์จ", brand: "OEM", price: 390, cost: 160, reorder: 10, img: null, stock: { "B1-SHOP": 9, "B3-SHOP": 7 } },
  { id: "P09", sku: "AC-OEM-000005", barcode: "8850002000058", name: "Powerbank 10,000mAh 22.5W", cat: "อุปกรณ์", sub: "Powerbank", brand: "OEM", price: 590, cost: 310, reorder: 6, img: null, stock: { "B1-SHOP": 7, "B2-SHOP": 4 } },
  { id: "P10", sku: "AC-OEM-000006", barcode: "8850002000065", name: "หูฟัง TWS Bluetooth 5.3", cat: "อุปกรณ์", sub: "หูฟัง", brand: "OEM", price: 790, cost: 380, reorder: 6, img: null, stock: { "B2-SHOP": 9, "B3-SHOP": 5 } },
  { id: "P11", sku: "SP-APPLE-000001", barcode: "8850003000019", name: "จอ iPhone 12 (OLED แท้)", cat: "อะไหล่", sub: "จอ", brand: "Apple", price: 3200, cost: 2400, reorder: 3, img: null, stock: { "B1-LAB": 2, "B2-LAB": 1 } },
  { id: "P12", sku: "SP-APPLE-000002", barcode: "8850003000026", name: "แบตเตอรี่ iPhone 12 (แท้)", cat: "อะไหล่", sub: "แบตเตอรี่", brand: "Apple", price: 990, cost: 520, reorder: 4, img: null, stock: { "B1-LAB": 2, "B2-LAB": 4 } },
  { id: "P13", sku: "SP-OEM-000003", barcode: "8850003000033", name: "แพรช่องชาร์จ Type-C (OEM)", cat: "อะไหล่", sub: "ช่องชาร์จ", brand: "OEM", price: 450, cost: 180, reorder: 5, img: null, stock: { "B1-LAB": 6, "B3-LAB": 3 } },
  { id: "P14", sku: "SP-SAMSUNG-000004", barcode: "8850003000040", name: "จอ Samsung A54 (งานแท้)", cat: "อะไหล่", sub: "จอ", brand: "Samsung", price: 2800, cost: 2050, reorder: 2, img: null, stock: { "B2-LAB": 2 } },
  { id: "P15", sku: "NB-APPLE-000001", barcode: "8850004000016", name: "MacBook Air M2 13\" 256GB", cat: "Notebook/MacBook", sub: "MacBook", brand: "Apple", price: 36900, wprice: 35500, cost: 32000, reorder: 1, img: null, stock: { "B1-A1": 2 } },
  { id: "P16", sku: "NB-ASUS-000002", barcode: "8850004000023", name: "ASUS Vivobook 15 i5/16GB/512GB", cat: "Notebook/MacBook", sub: "ASUS", brand: "ASUS", price: 18900, wprice: 17900, cost: 16200, reorder: 1, img: null, stock: { "B1-A1": 3 } },
];

const REPAIR_STATUSES = ["รับเครื่องแล้ว", "กำลังซ่อม", "รออะไหล่", "ซ่อมเสร็จ", "รอลูกค้ารับ", "ส่งมอบแล้ว"];
const ACCESSORY_OPTIONS = ["ซิมการ์ด", "เมมโมรี่การ์ด", "เคส", "ฟิล์มติดจอ", "สายชาร์จ", "หัวชาร์จ", "กล่อง", "หูฟัง"];

const REPAIRS = [
  { id: "RP-0291", branch: "B1", customer: "คุณเมย์", phone: "081-234-5678", brand: "Apple", device: "iPhone 13 Pro", imei: "356789104412345", code: "1234", color: "เทาเข้ม (Graphite)", accessories: ["ซิมการ์ด", "เคส"], img: null, issue: "จอแตก เปลี่ยนจอ", symptom: "ตกพื้น มุมขวาบนร้าว ทัชยังใช้ได้", est: 4500, deposit: 1000, status: "กำลังซ่อม", tech: "U4", ts: now - 1 * DAY, due: now + 1 * DAY, parts: [], note: "" },
  { id: "RP-0290", branch: "B1", customer: "คุณต้น", phone: "089-555-1122", brand: "Samsung", device: "Galaxy S22", imei: "354412098871234", code: "ไม่มีรหัส", color: "ดำ", accessories: [], img: null, issue: "เปลี่ยนแบตเตอรี่", symptom: "แบตหมดไว เครื่องร้อน", est: 1290, deposit: 0, status: "รออะไหล่", tech: "U6", ts: now - 2 * DAY, due: now + 2 * DAY, parts: [], note: "สั่งแบตจากศูนย์ รอ 2 วัน" },
  { id: "RP-0289", branch: "B2", customer: "คุณฝน", phone: "062-888-9900", brand: "Apple", device: "iPad Gen 9", imei: "353310087712098", code: "0000", color: "เงิน", accessories: ["สายชาร์จ", "หัวชาร์จ"], img: null, issue: "เปิดไม่ติด", symptom: "ชาร์จไม่เข้า เปิดไม่ติด", est: 1800, deposit: 500, status: "ซ่อมเสร็จ", tech: "U6", ts: now - 3 * DAY, due: now - 1 * DAY, parts: [{ productId: "P13", qty: 1 }], note: "เปลี่ยนแพรช่องชาร์จแล้ว ทดสอบผ่าน" },
  { id: "RP-0288", branch: "B1", customer: "คุณกอล์ฟ", phone: "091-777-3344", brand: "Xiaomi", device: "Redmi Note 12", imei: "351100992213456", code: "5678", color: "ฟ้า", accessories: ["เคส"], img: null, issue: "ช่องชาร์จหลวม", symptom: "ต้องขยับสายถึงชาร์จเข้า", est: 690, deposit: 0, status: "รอลูกค้ารับ", tech: "U4", ts: now - 4 * DAY, due: now - 2 * DAY, parts: [{ productId: "P13", qty: 1 }], note: "" },
  { id: "RP-0287", branch: "B3", customer: "คุณนิด", phone: "086-001-2233", brand: "Apple", device: "iPhone 12", imei: "356700112345987", code: "ไม่มีรหัส", color: "ขาว", accessories: [], img: null, issue: "เปลี่ยนแบตเตอรี่", symptom: "แบตบวม ดันจอ", est: 1290, deposit: 0, status: "ส่งมอบแล้ว", tech: "U4", ts: now - 6 * DAY, due: now - 4 * DAY, parts: [{ productId: "P12", qty: 1 }], note: "ส่งมอบ + เก็บเงินแล้ว" },
];

const mkItems = (...arr) => arr.map(([productId, qty, price]) => ({ productId, qty, price }));
const SALES = [
  { id: "INV-2506-0218", branch: "B1", ts: now - 0.1 * DAY, items: mkItems(["P05", 2, 190], ["P07", 1, 259]), discount: 0, pay: "เงินสด", customer: "ลูกค้าหน้าร้าน", by: "U3" },
  { id: "INV-2506-0217", branch: "B1", ts: now - 0.3 * DAY, items: mkItems(["P03", 1, 13499]), discount: 500, pay: "โอน/QR", customer: "คุณเจน 081-110-2233", by: "U3" },
  { id: "INV-2506-0216", branch: "B2", ts: now - 0.5 * DAY, items: mkItems(["P10", 1, 790], ["P08", 1, 390]), discount: 0, pay: "เงินสด", customer: "ลูกค้าหน้าร้าน", by: "U5" },
  { id: "INV-2506-0215", branch: "B1", ts: now - 1.2 * DAY, items: mkItems(["P01", 1, 21900]), discount: 900, pay: "บัตรเครดิต", customer: "คุณวุฒิ 089-444-5511", by: "U2" },
  { id: "INV-2506-0214", branch: "B3", ts: now - 1.5 * DAY, items: mkItems(["P07", 2, 259], ["P05", 1, 190]), discount: 0, pay: "โอน/QR", customer: "ลูกค้าหน้าร้าน", by: "U3" },
  { id: "INV-2506-0213", branch: "B2", ts: now - 2.3 * DAY, items: mkItems(["P04", 1, 7999], ["P06", 1, 290]), discount: 290, pay: "เงินสด", customer: "คุณบีม 064-991-8822", by: "U5" },
  { id: "INV-2506-0212", branch: "B1", ts: now - 3.4 * DAY, items: mkItems(["P09", 1, 590]), discount: 0, pay: "เงินสด", customer: "ลูกค้าหน้าร้าน", by: "U3" },
  { id: "INV-2506-0211", branch: "B3", ts: now - 4.1 * DAY, items: mkItems(["P02", 1, 13900]), discount: 400, pay: "ผ่อน 0%", customer: "คุณแพร 095-333-7788", by: "U2" },
  { id: "INV-2506-0210", branch: "B1", ts: now - 5.2 * DAY, items: mkItems(["P08", 2, 390], ["P05", 2, 190]), discount: 0, pay: "เงินสด", customer: "ลูกค้าหน้าร้าน", by: "U3" },
  { id: "INV-2506-0209", branch: "B2", ts: now - 6.4 * DAY, items: mkItems(["P03", 1, 13499]), discount: 0, pay: "โอน/QR", customer: "คุณโอ๊ค 080-220-1144", by: "U5" },
];

const BUYINS = [
  { id: "BI-0042", branch: "B1", ts: now - 0.2 * DAY, device: "iPhone 11 64GB", brand: "Apple", imei: "353299088812345", battery: 86, cond: "เกรด B · รอยขอบเล็กน้อย", price: 6500, seller: "คุณนุ่น 080-123-9988", sellerCard: "1103700xxxxx", imgCard: null, img: null, status: "รอตรวจสภาพ", by: "U3", kind: "รับซื้อ" },
  { id: "BI-0041", branch: "B1", ts: now - 1.1 * DAY, device: "Galaxy S21 FE", brand: "Samsung", imei: "350112099945671", battery: 95, cond: "เกรด A- · สภาพดี ครบกล่อง", price: 7800, seller: "คุณภูมิ 091-555-0011", sellerCard: "1409900xxxxx", imgCard: null, img: null, status: "รับเข้าสต็อกแล้ว", by: "U2", kind: "รับเทิร์น" },
  { id: "BI-0040", branch: "B2", ts: now - 2.5 * DAY, device: "iPhone XR 128GB", brand: "Apple", imei: "357700441234509", battery: 91, cond: "เกรด B · เปลี่ยนจอมา", price: 4200, seller: "คุณกิ๊ฟ 062-777-4455", sellerCard: "1101800xxxxx", imgCard: null, img: null, status: "รับเข้าสต็อกแล้ว", by: "U5", kind: "รับซื้อ" },
];

const MOVES = [
  { id: "MV-1021", ts: now - 0.1 * DAY, type: "ขายออก", productId: "P05", qty: -2, loc: "B1-SHOP", by: "U3", ref: "INV-2506-0218" },
  { id: "MV-1020", ts: now - 0.1 * DAY, type: "ขายออก", productId: "P07", qty: -1, loc: "B1-SHOP", by: "U3", ref: "INV-2506-0218" },
  { id: "MV-1019", ts: now - 0.3 * DAY, type: "ขายออก", productId: "P03", qty: -1, loc: "B1-SHOW", by: "U3", ref: "INV-2506-0217" },
  { id: "MV-1018", ts: now - 0.4 * DAY, type: "โอนย้าย", productId: "P07", qty: 6, loc: "B2-BACK → B1-SHOP", by: "U5", ref: "TF-0112" },
  { id: "MV-1017", ts: now - 0.9 * DAY, type: "รับเข้า", productId: "P05", qty: 20, loc: "B2-SHOP", by: "U5", ref: "PO-0078" },
  { id: "MV-1016", ts: now - 1.1 * DAY, type: "รับเข้า", productId: "P02", qty: 1, loc: "B1-SHOW", by: "U2", ref: "BI-0041" },
  { id: "MV-1015", ts: now - 1.2 * DAY, type: "ขายออก", productId: "P01", qty: -1, loc: "B1-SHOW", by: "U2", ref: "INV-2506-0215" },
  { id: "MV-1014", ts: now - 2.0 * DAY, type: "ใช้ซ่อม", productId: "P13", qty: -1, loc: "B2-LAB", by: "U6", ref: "RP-0289" },
  { id: "MV-1013", ts: now - 2.6 * DAY, type: "ปรับยอด", productId: "P06", qty: -1, loc: "B1-SHOP", by: "U5", ref: "นับสต็อก พบของหาย 1" },
  { id: "MV-1012", ts: now - 3.5 * DAY, type: "ใช้ซ่อม", productId: "P13", qty: -1, loc: "B1-LAB", by: "U4", ref: "RP-0288" },
];

// ตั้งค่าระบบ / ข้อมูลร้าน / รหัสผ่าน / เงื่อนไขใบซ่อม / PDPA
const DEFAULT_SETTINGS = {
  name: "SSPHONE",
  tagline: "ศูนย์ซื้อ–ขาย–ซ่อม มือถือครบวงจร",
  lineId: "",
  lineQr: null,
  logo: null, // null = ใช้โลโก้เริ่มต้น
  addr: "112/4 ถ.สีลม แขวงสุริยวงศ์ เขตบางรัก กทม. 10500",
  taxId: "0105561234567",
  tel: "02-234-5678",
  website: "www.ssphone.co.th",
  facebook: "facebook.com/ssphone",
  ownerEmail: "owner@ssphone.co.th",
  password: "S12345678",
  repairTerms: {
    responsibility: "ทางร้านไม่รับผิดชอบต่ออาการอื่น นอกเหนือจากอาการที่แจ้งซ่อม",
    warranty: "รับประกันตามที่ตกลงกันหลังซ่อมเสร็จ (ยกเว้นตกน้ำ ตกหล่น หรือใช้งานผิดปกติ)",
    pickup: "ไม่มารับเครื่องภายใน 30 วันหลังซ่อมเสร็จ ร้านขอสงวนสิทธิ์จัดการตามเหมาะสม",
    warrantyTime: "3 เดือน",
  },
  saleTerms: {
    device: "เครื่องมือสองรับประกันร้าน 30 วัน · กรุณาเก็บใบเสร็จไว้เป็นหลักฐานการรับประกัน",
    accessory: "อุปกรณ์เสริมรับประกันตามเงื่อนไขผู้ผลิต · เปลี่ยน/คืนภายใน 7 วันเมื่อมีใบเสร็จและสภาพสมบูรณ์",
  },
  pdpa: {
    collect: "ทางร้านเก็บรวบรวมข้อมูลส่วนบุคคลของท่าน ได้แก่ ชื่อ-นามสกุล, เบอร์โทรศัพท์, ข้อมูลอุปกรณ์ (IMEI, ยี่ห้อ, รุ่น, รหัสหน้าจอ) เพื่อวัตถุประสงค์ในการให้บริการซ่อมและติดต่อสื่อสาร",
    rights: "ท่านมีสิทธิในการเข้าถึง แก้ไข หรือขอให้ลบข้อมูลส่วนบุคคล โดยติดต่อทางร้าน",
    retention: "ข้อมูลจะถูกเก็บไว้เป็นระยะเวลา 3 ปี หรือจนกว่าท่านจะขอให้ลบข้อมูล",
    consent: "การลงชื่อในใบแจ้งซ่อมนี้ ถือเป็นการให้ความยินยอมในการเก็บและใช้ข้อมูลตาม PDPA",
  },
};

const DAY_MS = 86400000;
const PAWNS = [
  { id: "PW-0015", brand: "Apple", model: "iPhone 14 Pro Max 256GB", amount: 22000, paid: 0, start: now - 20 * DAY_MS, due: now + 10 * DAY_MS, note: "เครื่องศูนย์ไทย สภาพสวย", customer: "คุณวิทยา", phone: "081-555-2210", branch: "B1", status: "active" },
  { id: "PW-0016", brand: "Samsung", model: "Galaxy S23 Ultra", amount: 15000, paid: 0, start: now - 28 * DAY_MS, due: now + 1 * DAY_MS, note: "ครบกล่อง", customer: "คุณนภา", phone: "089-220-1144", branch: "B1", status: "active" },
  { id: "PW-0017", brand: "Apple", model: "iPhone 13 128GB", amount: 9000, paid: 0, start: now - 33 * DAY_MS, due: now - 3 * DAY_MS, note: "เลยกำหนด ติดต่อลูกค้าแล้ว", customer: "คุณเอก", phone: "086-771-9080", branch: "B2", status: "active" },
];
const DAILY_CLEARS = (() => {
  const arr = [];
  const z = (n, w = 4) => String(n).padStart(w, "0");
  const d0 = new Date(); d0.setHours(0, 0, 0, 0);
  const mk = (id, branch, dayAgo, total, cash, transfer, by) => ({ id, branch, date: d0.getTime() - dayAgo * DAY_MS, total, cash, transfer, photos: [], note: "", by, ts: d0.getTime() - dayAgo * DAY_MS + 68400000 });
  for (let d = 1; d <= 6; d++) {
    arr.push(mk(`DC-${z(40 - d * 2)}`, "B1", d, 18000 + (d * 1700) % 9000, 9000 + (d * 800) % 5000, 9000 + (d * 900) % 4000, "U3"));
    arr.push(mk(`DC-${z(41 - d * 2)}`, "B2", d, 12000 + (d * 1300) % 7000, 6000 + (d * 600) % 4000, 6000 + (d * 700) % 3000, "U2"));
  }
  return arr;
})();

const SEED = {
  branches: BRANCHES, locations: LOCATIONS, roles: ROLES, users: USERS,
  cats: CATS, skuPool: SKU_POOL,
  products: PRODUCTS, repairs: REPAIRS, sales: SALES, buyins: BUYINS, moves: MOVES,
  pawns: PAWNS, dailyClears: DAILY_CLEARS,
  settings: DEFAULT_SETTINGS,
  seq: { sale: 219, repair: 292, buyin: 43, move: 1022, transfer: 113, branch: 4, product: 17, loc: 1, user: 7, pawn: 18, clear: 40 },
};

/* ── helpers ── */
const locName = (st, id) => {
  const l = st.locations.find((x) => x.id === id);
  if (!l) return id;
  const b = st.branches.find((bb) => bb.id === l.branch);
  return `${b ? b.short : "—"} · ${l.name}`;
};
const prodOf = (st, id) => st.products.find((p) => p.id === id);
const userOf = (st, id) => st.users.find((u) => u.id === id) || { name: "—" };
// สเปกย่อ (สี · ความจุ · IMEI/SN) — ดึงจากสินค้า หรือจากรายการรับซื้อต้นทาง
const specOf = (st, p) => {
  if (!p) return "";
  let color = p.color, storage = p.storage, imei = p.imei;
  const m = (p.name || "").match(/BI-\d+/);
  if (m && st.buyins) {
    const bi = st.buyins.find((b) => b.id === m[0]);
    if (bi) { color = color || bi.color; storage = storage || bi.storage; imei = imei || bi.imei; }
  }
  const bits = [];
  if (color) bits.push(color);
  if (storage) bits.push(storage);
  if (imei) bits.push("IMEI/SN: " + imei);
  return bits.join(" · ");
};
const branchOf = (st, id) => st.branches.find((b) => b.id === id) || { name: "(สาขาถูกลบ)", short: "—", addr: "", tel: "", taxId: "" };
const stockTotal = (p) => Object.values(p.stock).reduce((a, b) => a + b, 0);
const stockInBranch = (st, p, branchId) => Object.entries(p.stock).reduce((a, [loc, q]) => a + (loc.startsWith(branchId) ? q : 0), 0);
const saleTotal = (s) => s.items.reduce((a, i) => a + i.qty * i.price, 0) - (s.discount || 0);
const pad = (n, w = 4) => String(n).padStart(w, "0");

const brandToken = (b) => (b || "OEM").replace(/[^A-Za-z0-9ก-๙]/g, "").toUpperCase() || "OEM";
const catPrefix = (st, catName) => {
  const c = st.cats.find((x) => x.name === catName);
  return c ? c.prefix : "GN";
};
// ดูเลขถัดไป (ไม่ขยับ pool) — ใช้แสดง preview ในฟอร์ม
const peekSku = (st, catName, brand) => {
  const pf = catPrefix(st, catName);
  const pool = st.skuPool[pf] || { next: 1, freed: [] };
  const n = pool.freed.length ? Math.min(...pool.freed) : pool.next;
  return `${pf}-${brandToken(brand)}-${pad(n, 6)}`;
};

// ── รหัสลับต้นทุน (12 หลัก) ──
// หลัก 2,4,6 = ราคาส่ง 3 หลักแรก · หลัก 8,10,12 = ทุน 3 หลักแรก · หลักคี่ = สุ่ม
function digits3(n) {
  const s = String(Math.floor(Math.abs(Number(n) || 0)));
  return s.length >= 3 ? s.slice(0, 3) : s.padStart(3, "0");
}
function genSecretCode(product) {
  const wsrc = (product.wprice != null && product.wprice !== "") ? product.wprice : product.price;
  const w = digits3(wsrc);
  const c = digits3(product.cost);
  const r = () => String(Math.floor(Math.random() * 10));
  // ลำดับหลัก 1..12
  return r() + w[0] + r() + w[1] + r() + w[2] + r() + c[0] + r() + c[1] + r() + c[2];
}
// stamp รหัสลับให้สินค้าใน SEED (ครั้งเดียวตอนโหลด)
try {
  SEED.products.forEach((p) => { if (!p.secretCode) p.secretCode = genSecretCode(p); });
} catch (e) { console.error("secretCode stamp error:", e); }

/* ── reducer ── */
function reducer(st, a) {
  if (!st || !st.users) {
    return structuredClone(SEED);
  }
  // กู้คืนข้อมูลทั้งหมดจากไฟล์สำรอง — แทนที่ state ทั้งก้อน
  if (a.type === "RESTORE_ALL") {
    try {
      const next = structuredClone(a.data);
      // กันข้อมูลเก่าที่ field ไม่ครบ
      if (!next.seq) next.seq = structuredClone(SEED.seq);
      if (!next.settings) next.settings = structuredClone(SEED.settings);
      return next;
    } catch (e) { return st; }
  }
  const cl = structuredClone(st);
  const addMove = (type, productId, qty, loc, by, ref) => {
    cl.moves.unshift({ id: `MV-${pad(cl.seq.move)}`, ts: Date.now(), type, productId, qty, loc, by, ref });
    cl.seq.move++;
  };
  const bumpStock = (productId, locId, delta) => {
    const p = cl.products.find((x) => x.id === productId);
    p.stock[locId] = (p.stock[locId] || 0) + delta;
    if (p.stock[locId] <= 0) delete p.stock[locId];
  };
  // จองเลขรัน SKU (ใช้เลขว่างจากการลบก่อน)
  const takeSku = (catName, brand) => {
    const pf = catPrefix(cl, catName);
    if (!cl.skuPool[pf]) cl.skuPool[pf] = { next: 1, freed: [] };
    const pool = cl.skuPool[pf];
    let n;
    if (pool.freed.length) { n = Math.min(...pool.freed); pool.freed = pool.freed.filter((x) => x !== n); }
    else { n = pool.next; pool.next++; }
    return `${pf}-${brandToken(brand)}-${pad(n, 6)}`;
  };
  const newProductId = () => { const id = `P${pad(cl.seq.product, 2)}`; cl.seq.product++; return id; };

  switch (a.type) {
    case "SALE": {
      const id = `INV-2506-${pad(cl.seq.sale)}`;
      cl.seq.sale++;
      cl.sales.unshift({ ...a.sale, id, ts: Date.now() });
      a.sale.items.forEach((it) => {
        // ตัดสต็อกจาก Location ที่กรอกมา ถ้าไม่มีของจริงให้หา Location ที่มีสต็อกแทน (กันกรณีอ้างอิง Location ผิด)
        const prod = cl.products.find((x) => x.id === it.productId);
        let loc = it.locId;
        if (prod && (prod.stock[loc] || 0) < it.qty) {
          const best = Object.entries(prod.stock).filter(([, q]) => q > 0).sort((aa, bb) => bb[1] - aa[1])[0];
          if (best) loc = best[0];
        }
        bumpStock(it.productId, loc, -it.qty);
        addMove("ขายออก", it.productId, -it.qty, loc, a.sale.by, id);
      });
      return { ...cl, lastSaleId: id };
    }
    case "SALE_MARK_PAID": {
      const s = cl.sales.find((x) => x.id === a.id);
      if (s) { s.pending = false; s.pay = a.pay || "เงินสด"; s.paidAt = Date.now(); }
      return cl;
    }
    case "WHOLESALE_RETURN_ITEM": {
      // คืนสินค้าชิ้นนั้นกลับเข้าสต็อก แล้วเอาออกจากบิลรอจ่าย
      const s = cl.sales.find((x) => x.id === a.id);
      if (s && s.items[a.idx]) {
        const it = s.items[a.idx];
        bumpStock(it.productId, it.locId, it.qty);
        addMove("รับคืน", it.productId, it.qty, it.locId, a.by, s.id);
        s.items.splice(a.idx, 1);
        if (s.items.length === 0) cl.sales = cl.sales.filter((x) => x.id !== s.id);
      }
      return cl;
    }
    case "WHOLESALE_SETTLE": {
      // รวมสินค้าทั้งหมดของร้านนั้นเข้าบิลเดียว แล้วทำเครื่องหมายว่าชำระแล้ว
      const sales = (a.ids || []).map((id) => cl.sales.find((x) => x.id === id)).filter(Boolean);
      if (sales.length) {
        const main = sales[0];
        for (let k = 1; k < sales.length; k++) {
          main.items.push(...sales[k].items);
          main.discount = (main.discount || 0) + (sales[k].discount || 0);
        }
        main.pending = false; main.pay = a.pay || "เงินสด"; main.paidAt = Date.now();
        const rm = sales.slice(1).map((x) => x.id);
        cl.sales = cl.sales.filter((x) => !rm.includes(x.id));
      }
      return cl;
    }
    case "BUYIN_ADD": {
      const id = `BI-${pad(cl.seq.buyin)}`;
      cl.seq.buyin++;
      cl.buyins.unshift({ ...a.buyin, id, ts: Date.now(), status: "รอตรวจสภาพ" });
      return { ...cl, lastBuyinId: id };
    }
    case "BUYIN_ACCEPT": {
      const bi = cl.buyins.find((x) => x.id === a.id);
      bi.status = "รับเข้าสต็อกแล้ว";
      const pid = newProductId();
      const isNb = bi.kind === "Notebook/MacBook";
      const cat = a.cat || (isNb ? "Notebook/MacBook" : "เครื่องมือสอง");
      const brand = a.brand || bi.brand || "อื่นๆ";
      const nbProd = {
        id: pid, sku: takeSku(cat, brand), barcode: `885000${Date.now() % 10000000}`, addedAt: Date.now(),
        name: `${bi.device} (มือสอง · ${bi.id})`, cat, sub: brand,
        color: bi.color || "", storage: bi.storage || "", imei: bi.imei || "",
        brand, price: a.price, wprice: a.wprice != null ? a.wprice : null, cost: bi.price, reorder: 0, img: bi.img || null, stock: { [a.locId]: 1 },
      };
      nbProd.secretCode = genSecretCode(nbProd);
      cl.products.push(nbProd);
      addMove("รับเข้า", pid, 1, a.locId, a.by, bi.id);
      return cl;
    }
    case "BUYIN_SET": {
      const bi = cl.buyins.find((x) => x.id === a.id);
      if (bi) Object.assign(bi, a.patch);
      return cl;
    }
    case "BUYIN_DEL": {
      cl.buyins = cl.buyins.filter((x) => x.id !== a.id);
      return cl;
    }
    case "REPAIR_ADD": {
      const id = `RP-${pad(cl.seq.repair)}`;
      cl.seq.repair++;
      cl.repairs.unshift({ ...a.repair, id, ts: Date.now(), parts: [], status: "รับเครื่องแล้ว" });
      return { ...cl, lastRepairId: id };
    }
    case "REPAIR_SET": {
      const r = cl.repairs.find((x) => x.id === a.id);
      Object.assign(r, a.patch);
      // บันทึกวันที่ส่งมอบลูกค้า (ใช้คิดยอดขายช่างในวันที่ส่งมอบจริง)
      if (a.patch.status === "ส่งมอบแล้ว" && !r.deliveredAt) r.deliveredAt = Date.now();
      return cl;
    }
    case "REPAIR_INVOICE": {
      // ออกเลขที่ใบเสร็จ INV ให้งานซ่อม (ใช้ลำดับเดียวกับการขายเครื่อง/อุปกรณ์) อ้างอิง RP เดิม
      const r = cl.repairs.find((x) => x.id === a.id);
      if (r && !r.invNo) { r.invNo = `INV-2506-${pad(cl.seq.sale)}`; cl.seq.sale++; }
      if (r && a.pay) r.pay = a.pay;
      if (r && a.discount != null) r.discount = Number(a.discount) || 0;
      return cl;
    }
    case "REPAIR_USE_PART": {
      const r = cl.repairs.find((x) => x.id === a.id);
      r.parts.push({ productId: a.productId, qty: a.qty });
      bumpStock(a.productId, a.locId, -a.qty);
      addMove("ใช้ซ่อม", a.productId, -a.qty, a.locId, a.by, a.id);
      return cl;
    }
    case "TRANSFER": {
      const ref = `TF-${pad(cl.seq.transfer)}`;
      cl.seq.transfer++;
      bumpStock(a.productId, a.from, -a.qty);
      bumpStock(a.productId, a.to, a.qty);
      addMove("โอนย้าย", a.productId, a.qty, `${a.from} → ${a.to}`, a.by, ref);
      return cl;
    }
    case "ADJUST": {
      bumpStock(a.productId, a.locId, a.delta);
      addMove(a.delta > 0 ? "รับเข้า" : "ปรับยอด", a.productId, a.delta, a.locId, a.by, a.reason || "ปรับยอดสต็อก");
      return cl;
    }
    case "PRODUCT_SET": {
      const p = cl.products.find((x) => x.id === a.id);
      Object.assign(p, a.patch);
      // ราคาส่ง/ราคาขาย/ทุน เปลี่ยน → สร้างรหัสลับต้นทุนใหม่ตามราคาล่าสุด
      if ("wprice" in a.patch || "price" in a.patch || "cost" in a.patch) {
        p.secretCode = genSecretCode(p);
      }
      return cl;
    }
    case "PRODUCT_ADD": {
      const pid = newProductId();
      const sku = takeSku(a.product.cat, a.product.brand);
      const prod = { ...a.product, id: pid, sku, addedAt: Date.now(), stock: {} };
      prod.secretCode = genSecretCode(prod);
      cl.products.push(prod);
      if (a.product.initQty > 0 && a.product.initLoc) {
        prod.stock = { [a.product.initLoc]: a.product.initQty };
        addMove("รับเข้า", pid, a.product.initQty, a.product.initLoc, a.by, "สินค้าใหม่");
      }
      return { ...cl, lastSku: sku };
    }
    case "PRODUCT_DEL": {
      const p = cl.products.find((x) => x.id === a.id);
      if (p) {
        // คืนเลขรันกลับ pool เพื่อนำมาใช้ใหม่
        const m = (p.sku || "").match(/^([A-Z]+)-.*-(\d+)$/);
        if (m && cl.skuPool[m[1]]) {
          const n = parseInt(m[2], 10);
          if (!cl.skuPool[m[1]].freed.includes(n)) cl.skuPool[m[1]].freed.push(n);
          cl.skuPool[m[1]].freed.sort((x, y) => x - y);
        }
        cl.products = cl.products.filter((x) => x.id !== a.id);
      }
      return cl;
    }
    case "CAT_ADD": {
      if (!cl.cats.some((c) => c.name === a.name)) {
        cl.cats.push({ name: a.name, prefix: a.prefix, subs: [], parts: !!a.parts });
        if (!cl.skuPool[a.prefix]) cl.skuPool[a.prefix] = { next: 1, freed: [] };
      }
      return cl;
    }
    case "SUB_ADD": {
      const c = cl.cats.find((x) => x.name === a.cat);
      if (c && !c.subs.includes(a.sub)) c.subs.push(a.sub);
      return cl;
    }
    case "SUB_DEL": {
      const c = cl.cats.find((x) => x.name === a.cat);
      if (c) c.subs = c.subs.filter((s) => s !== a.sub);
      return cl;
    }
    case "BRANCH_ADD": {
      const id = `B${cl.seq.branch}`;
      cl.seq.branch++;
      cl.branches.push({ id, name: a.branch.name, short: a.branch.short || a.branch.name.replace("สาขา", ""), tel: a.branch.tel || "", addr: a.branch.addr || "", taxId: a.branch.taxId || "" });
      const letter = String.fromCharCode(64 + cl.branches.length); // A,B,C,...
      LOC_TYPES.forEach((lt, li) => cl.locations.push({ id: `${id}-${lt.code}`, branch: id, name: lt.name, code: `${letter}${li + 1}` }));
      return cl;
    }
    case "BRANCH_SET": {
      const b = cl.branches.find((x) => x.id === a.id);
      Object.assign(b, a.patch);
      return cl;
    }
    case "BRANCH_DEL": {
      cl.branches = cl.branches.filter((b) => b.id !== a.id);
      cl.locations = cl.locations.filter((l) => l.branch !== a.id);
      // ย้ายพนักงานไปสาขาแรกที่เหลือ
      const fb = cl.branches[0];
      cl.users.forEach((u) => { if (u.branch === a.id) u.branch = fb ? fb.id : ""; });
      return cl;
    }
    case "LOC_ADD": {
      const letter = String.fromCharCode(65 + cl.branches.findIndex((b) => b.id === a.branch));
      const nInBranch = cl.locations.filter((l) => l.branch === a.branch).length;
      const code = `${letter}${nInBranch + 1}`;
      const id = `${a.branch}-X${cl.seq.loc}`;
      cl.seq.loc++;
      cl.locations.push({ id, branch: a.branch, name: a.name, code });
      return cl;
    }
    case "LOC_DEL": {
      cl.locations = cl.locations.filter((l) => l.id !== a.id);
      // ล้างสต็อกที่ผูกกับ Location นี้ออกจากทุกสินค้า
      cl.products.forEach((p) => { if (p.stock[a.id]) delete p.stock[a.id]; });
      return cl;
    }
    case "CAT_SET": {
      const c = cl.cats.find((x) => x.name === a.name);
      if (c) {
        const old = c.name;
        if (a.patch.name && a.patch.name !== old) {
          cl.products.forEach((p) => { if (p.cat === old) p.cat = a.patch.name; });
        }
        Object.assign(c, a.patch);
        if (a.patch.prefix && !cl.skuPool[a.patch.prefix]) cl.skuPool[a.patch.prefix] = { next: 1, freed: [] };
      }
      return cl;
    }
    case "CAT_DEL": {
      cl.cats = cl.cats.filter((c) => c.name !== a.name);
      return cl;
    }
    case "SETTINGS_SET": {
      cl.settings = { ...cl.settings, ...a.patch };
      return cl;
    }
    case "ROLE_PAGES_SET": {
      if (cl.roles[a.role] && a.role !== "owner") cl.roles[a.role].pages = a.pages;
      return cl;
    }
    case "USER_SET": {
      const u = cl.users.find((x) => x.id === a.id);
      Object.assign(u, a.patch);
      return cl;
    }
    case "ROLE_PERM_SET": {
      const r = cl.roles[a.role];
      if (a.role !== "owner") {
        if (a.perm === "dashboardAccess") r.dashboardAccess = a.value;
        if (a.perm === "stockAccess") r.stockAccess = a.value;
        if (a.perm === "partsAccess") r.partsAccess = a.value;
        if (a.perm === "allBranch") r.allBranch = a.value;
      }
      return cl;
    }
    case "USER_ADD": {
      const id = `U${cl.seq.user || (cl.users.length + 1)}`;
      cl.seq.user = (cl.seq.user || cl.users.length + 1) + 1;
      cl.users.push({ ...a.user, id });
      return cl;
    }
    case "PAWN_ADD": {
      const id = `PW-${pad(cl.seq.pawn)}`;
      cl.seq.pawn++;
      cl.pawns.unshift({ ...a.pawn, id, status: "active", ts: Date.now() });
      return { ...cl, lastPawnId: id };
    }
    case "PAWN_SET": {
      const pw = cl.pawns.find((x) => x.id === a.id);
      if (pw) Object.assign(pw, a.patch);
      return cl;
    }
    case "PAWN_DEL": {
      cl.pawns = cl.pawns.filter((x) => x.id !== a.id);
      return cl;
    }
    case "DAILYCLEAR_ADD": {
      const id = `DC-${pad(cl.seq.clear)}`;
      cl.seq.clear++;
      cl.dailyClears.unshift({ ...a.clear, id, ts: Date.now() });
      return { ...cl, lastClearId: id };
    }
    case "RESET": return structuredClone(SEED);
    default: return st;
  }
}

const STORE_KEY = "ssphone-v3";
// migrate — เติม field ที่ขาดให้ข้อมูลเก่า โดยไม่ล้างข้อมูลผู้ใช้
function migrate(v) {
  if (!v.settings) v.settings = structuredClone(DEFAULT_SETTINGS);
  if (v.settings && !v.settings.saleTerms) v.settings.saleTerms = structuredClone(DEFAULT_SETTINGS.saleTerms);
  if (v.products) v.products.forEach((p) => { if (!p.secretCode) p.secretCode = genSecretCode(p); });
  if (v.seq && v.seq.loc == null) v.seq.loc = 1;
  if (v.seq && v.seq.user == null) v.seq.user = (v.users ? v.users.length : 6) + 1;
  // เพิ่ม parts page + partsAccess ให้ role
  if (v.roles && !v.roles.owner.partsAccess) {
    for (const r of Object.values(v.roles)) {
      if (!r.partsAccess) r.partsAccess = r.stockAccess === "all" ? "all" : "own";
      if (!r.pages.includes("parts") && (r.stockAccess === "all" || r.role === "owner")) r.pages.push("parts");
    }
  }
  if (!v.pawns) v.pawns = structuredClone(PAWNS);
  if (!v.dailyClears) v.dailyClears = structuredClone(DAILY_CLEARS);
  if (v.settings && v.settings.taxId == null) v.settings.taxId = "0105561234567";
  // ทำเครื่องหมายหมวด "อะไหล่" เป็นหมวดสำหรับสต็อกอะไหล่
  if (v.cats) { const sp = v.cats.find((c) => c.name === "อะไหล่"); if (sp && sp.parts == null) sp.parts = true; }
  // เพิ่มหน้าใบกำกับภาษีให้เจ้าของร้านและผู้จัดการ
  if (v.roles) {
    for (const key of ["owner", "manager"]) {
      if (v.roles[key] && !v.roles[key].pages.includes("invoices")) v.roles[key].pages.push("invoices");
    }
    // เพิ่มเมนูขายส่ง (POS) ให้ role ที่ขายของได้ (ต่อท้าย pos)
    for (const key of ["owner", "manager", "sales"]) {
      const r = v.roles[key];
      if (r && r.pages.includes("pos") && !r.pages.includes("pos_wholesale")) {
        r.pages.splice(r.pages.indexOf("pos") + 1, 0, "pos_wholesale");
      }
    }
  }
  // เติมหมวด Notebook/MacBook ระหว่างเครื่องมือสอง กับ อุปกรณ์
  if (v.cats && !v.cats.some((c) => c.name === "Notebook/MacBook")) {
    const nb = { name: "Notebook/MacBook", prefix: "NB", subs: ["MacBook", "ASUS", "Acer", "Lenovo", "HP", "Dell", "MSI"] };
    const idx = v.cats.findIndex((c) => c.name === "อุปกรณ์");
    if (idx >= 0) v.cats.splice(idx, 0, nb); else v.cats.push(nb);
  }
  // แก้หมวดสินค้าที่รับเข้าจากการรับซื้อ Notebook/MacBook แต่ถูกบันทึกผิดเป็น "เครื่องมือสอง"
  if (v.products && v.buyins) {
    const nbIds = new Set(v.buyins.filter((b) => b.kind === "Notebook/MacBook").map((b) => b.id));
    for (const p of v.products) {
      if (p.cat === "เครื่องมือสอง") {
        const m = (p.name || "").match(/BI-\d+/);
        if (m && nbIds.has(m[0])) { p.cat = "Notebook/MacBook"; }
      }
    }
  }
  // เติมวันที่ส่งมอบให้งานซ่อมที่ส่งมอบแล้วแต่ยังไม่มี deliveredAt (ใช้คิดยอดขายช่าง)
  if (v.repairs) v.repairs.forEach((r) => { if (r.status === "ส่งมอบแล้ว" && !r.deliveredAt) r.deliveredAt = r.due || r.ts; });
  if (v.seq && v.seq.pawn == null) v.seq.pawn = 18;
  if (v.seq && v.seq.clear == null) v.seq.clear = 40;
  // เติมหน้า income/pawns/parts ให้ role เดิม
  if (v.roles) {
    const ensure = (rk, page, after) => {
      const r = v.roles[rk]; if (!r || r.pages.includes(page)) return;
      const idx = r.pages.indexOf(after);
      if (idx >= 0) r.pages.splice(idx + 1, 0, page); else r.pages.push(page);
    };
    ["owner", "manager", "sales"].forEach((rk) => { ensure(rk, "income", "dashboard"); ensure(rk, "pawns", "buyin"); });
    ["owner", "manager", "tech", "stock"].forEach((rk) => { ensure(rk, "parts", "stock"); });
    // เติม partsAccess ให้ role ที่ยังไม่มี
    Object.entries(v.roles).forEach(([rk, r]) => {
      if (!r.partsAccess) r.partsAccess = rk === "owner" || r.stockAccess === "all" ? "all" : "own";
    });
  }
  // เพิ่มหน้า settings ให้ทุก role เจ้าของ
  if (v.roles && v.roles.owner && !v.roles.owner.pages.includes("settings")) v.roles.owner.pages.push("settings");
  // เติม username/password ให้ผู้ใช้เก่าที่ยังไม่มี
  const seedCreds = { U1: ["owner", "S12345678"], U2: ["vipha", "Vipha1234"], U3: ["pond", "Pond1234"], U4: ["boy", "Boy12345"], U5: ["nat", "Nat12345"], U6: ["keng", "Keng1234"] };
  (v.users || []).forEach((u, i) => {
    if (!u.username) u.username = (seedCreds[u.id] && seedCreds[u.id][0]) || `user${u.id}`.toLowerCase();
    if (!u.password) u.password = (seedCreds[u.id] && seedCreds[u.id][1]) || `Pass${1000 + i}`;
  });
  return v;
}
// ขยับ timestamp ข้อมูลเดโมให้สดเสมอ (ถ้าเปิดแอปห่างจากครั้งก่อนนาน ๆ)
function freshen(v) {
  try {
    // anchor the newest SALE to ~1.5h ago so the demo always shows today's activity
    const newestSale = Math.max(...v.sales.map((s) => s.ts));
    const target = Date.now() - DAY * 0.06;
    const shift = target - newestSale;
    if (Math.abs(shift) > DAY * 0.25) {
      ["sales", "moves", "buyins"].forEach((k) => (v[k] || []).forEach((x) => { x.ts += shift; }));
      (v.repairs || []).forEach((r) => { r.ts += shift; if (r.due) r.due += shift; });
    }
  } catch (e) {}
  return v;
}
function loadInitial() {
  // สำหรับตอนนี้ใช้ SEED เสมอเพื่อให้ login ทำงาน
  return structuredClone(SEED);
}

const StoreCtx = React.createContext(null);

function StoreProvider({ children }) {
  const [st, dispatch] = React.useReducer(reducer, structuredClone(SEED));

  // Save to localStorage on every state change
  React.useLayoutEffect(() => {
    try {
      localStorage.setItem(STORE_KEY, JSON.stringify(st));
    } catch (e) {}
  }, [st]);

  return (
    <StoreCtx.Provider value={{ st, dispatch }}>
      {children}
    </StoreCtx.Provider>
  );
}

const useStore = () => {
  const ctx = React.useContext(StoreCtx);
  if (!ctx) throw new Error("useStore must be inside StoreProvider");
  return ctx;
}

Object.assign(window, {
  StoreProvider, useStore, REPAIR_STATUSES, ACCESSORY_OPTIONS, BRANDS,
  locName, prodOf, userOf, branchOf, stockTotal, stockInBranch, saleTotal, ssPad: pad, specOf,
  peekSku, catPrefix,
  logoSrc: (st) => (st.settings && st.settings.logo) || "assets/ssphone-logo.png",
  shopName: (st) => (st.settings && st.settings.name) || "SSPHONE",
  usernameTaken: (st, uname, exceptId) => st.users.some((u) => u.id !== exceptId && (u.username || "").toLowerCase() === (uname || "").toLowerCase()),
  passwordTaken: (st, pw, exceptId) => st.users.some((u) => u.id !== exceptId && u.password === pw),
});
