"""One-off seed: registers translation_keys English source_text and hi/mr/hinglish
translation_values for 71 "app"-namespace UI strings across Administration and
Vendor-Contracts pages (View Role, View User, User Profile, Executive Dashboard,
Edit Role, Permission Catalog, Create Role, Edit User, Org Units List, Purchase
Order Details, Invoice Verification, Add Purchase Order, Contracts, Purchase
Orders) that are already wrapped in t(key, englishFallback) calls in the code
but were never seeded, so hi/mr/hinglish users see raw English.

Usage: docker compose exec api python seed_translations_gap_batch5_admin_vendor.py
"""
import asyncio
import uuid
from datetime import datetime, timezone

from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine

from ams.core.config import settings

NAMESPACE = "app"

# key -> (english, hi, mr, hinglish)
GAP_BATCH5_ADMIN_VENDOR = {
    "administration.common.clear": (
        "Clear",
        "साफ़ करें",
        "साफ करा",
        "Clear karein",
    ),
    "administration.permissions.seedRoles": (
        "Reseed Standard Roles",
        "Reseed Standard Roles",
        "Reseed Standard Roles",
        "Reseed Standard Roles",
    ),
    "administration.permissions.seeding": (
        "Reseeding...",
        "Reseed हो रहा है...",
        "Reseed होत आहे...",
        "Reseed ho raha hai...",
    ),
    "administration.roles.info.field2Desc": (
        "System roles ship with the product and can't be renamed/deleted; Custom roles are tenant-defined.",
        "System roles product के साथ आते हैं और इन्हें rename/delete नहीं किया जा सकता; Custom roles tenant द्वारा परिभाषित होते हैं।",
        "System roles product सोबत येतात आणि त्यांना rename/delete करता येत नाही; Custom roles tenant द्वारे परिभाषित केले जातात.",
        "System roles product ke saath aate hain aur inhe rename/delete nahi kiya ja sakta; Custom roles tenant dwara define kiye jaate hain.",
    ),
    "administration.roles.info.field3Desc": (
        "Raw permission keys the role currently holds; unrecognized keys (not in the catalog) are grouped under \"Other\".",
        "वे raw permission keys जो role वर्तमान में रखता है; catalog में न मिलने वाली keys को \"Other\" के अंतर्गत समूहीकृत किया जाता है।",
        "role सध्या धारण करत असलेल्या raw permission keys; catalog मध्ये न सापडणाऱ्या keys \"Other\" अंतर्गत गटबद्ध केल्या जातात.",
        "Wo raw permission keys jo role currently hold karta hai; catalog mein na milne wali keys ko \"Other\" ke under grouped kiya jaata hai.",
    ),
    "administration.roles.info.field3Name": (
        "Permissions",
        "Permissions",
        "Permissions",
        "Permissions",
    ),
    "administration.roles.info.field4Desc": (
        "Every user whose account currently has this role — click a row to open that user's profile.",
        "हर वह user जिसके account में वर्तमान में यह role है — उस user की profile खोलने के लिए row पर click करें।",
        "प्रत्येक तो user ज्याच्या account मध्ये सध्या ही role आहे — त्या user ची profile उघडण्यासाठी row वर click करा.",
        "Har wo user jiske account mein currently ye role hai — us user ki profile kholne ke liye row par click karein.",
    ),
    "administration.roles.info.field4Name": (
        "Assigned Users",
        "असाइन किए गए Users",
        "असाइन केलेले Users",
        "Assigned Users",
    ),
    "administration.roles.info.step2Caption": (
        "Permission keys attached via Role Permissions / the Permission Catalog.",
        "Permission keys Role Permissions / Permission Catalog के माध्यम से जोड़ी जाती हैं।",
        "Permission keys Role Permissions / Permission Catalog द्वारे जोडल्या जातात.",
        "Permission keys Role Permissions / Permission Catalog ke through attach hoti hain.",
    ),
    "administration.roles.info.step3Caption": (
        "Users get this role, inheriting exactly the permissions shown here.",
        "Users को यह role मिलता है, और वे ठीक वही permissions inherit करते हैं जो यहाँ दिखाई गई हैं।",
        "Users ना ही role मिळते, आणि ते इथे दाखवलेल्या नेमक्या permissions inherit करतात.",
        "Users ko ye role milta hai, aur wo exactly wahi permissions inherit karte hain jo yahan dikhayi gayi hain.",
    ),
    "administration.roles.info.step3Label": (
        "Users Assigned",
        "Users असाइन किए गए",
        "Users असाइन केले",
        "Users Assign Hue",
    ),
    "administration.roles.info.step4Caption": (
        "This page — a live, read-only audit view of the two steps above.",
        "यह page — ऊपर दिए गए दो steps का एक live, read-only audit दृश्य।",
        "हे page — वरील दोन steps चे live, read-only audit दृश्य.",
        "Ye page — upar diye gaye do steps ka ek live, read-only audit view.",
    ),
    "administration.roles.info.step4Label": (
        "Inspected Here",
        "यहाँ निरीक्षण किया गया",
        "इथे तपासणी केली",
        "Yahan Inspect Hota Hai",
    ),
    "administration.roles.searchPermissions": (
        "Search permissions...",
        "Permissions खोजें...",
        "Permissions शोधा...",
        "Permissions search karein...",
    ),
    "administration.users.info.field2Name": (
        "Assigned Role",
        "Assigned Role",
        "Assigned Role",
        "Assigned Role",
    ),
    "administration.users.info.field3Name": (
        "Assigned Roles (sidebar)",
        "Assigned Roles (sidebar)",
        "Assigned Roles (sidebar)",
        "Assigned Roles (sidebar)",
    ),
    "administration.users.info.field4Name": (
        "Status",
        "स्थिति",
        "स्थिती",
        "Status",
    ),
    "administration.users.info.field5Name": (
        "HR & Contact fields",
        "HR और Contact fields",
        "HR आणि Contact fields",
        "HR & Contact fields",
    ),
    "administration.users.info.step2Caption": (
        "Pick a role from the dropdown, then click Assign.",
        "dropdown से एक role चुनें, फिर Assign पर क्लिक करें।",
        "dropdown मधून एक role निवडा, नंतर Assign वर क्लिक करा.",
        "Dropdown se ek role pick karein, phir Assign par click karein.",
    ),
    "administration.users.info.step3Caption": (
        "Click the x on a role badge to revoke it instantly.",
        "role badge पर मौजूद x पर क्लिक करके इसे तुरंत revoke करें।",
        "role badge वरील x वर क्लिक करून ते त्वरित revoke करा.",
        "Role badge par x par click karke usse turant revoke karein.",
    ),
    "administration.users.info.step3Label": (
        "Remove Role",
        "Role हटाएँ",
        "Role काढून टाका",
        "Role Remove Karein",
    ),
    "administration.users.info.step4Caption": (
        "Report credential mismatches to SecOps per the notice below.",
        "नीचे दिए गए notice के अनुसार credential mismatches की सूचना SecOps को दें।",
        "खालील notice नुसार credential mismatches बद्दल SecOps ला कळवा.",
        "Neeche diye gaye notice ke hisaab se credential mismatches SecOps ko report karein.",
    ),
    "administration.users.info.step4Label": (
        "Escalate Issues",
        "समस्याओं को एस्केलेट करें",
        "समस्या एस्कलेट करा",
        "Issues Escalate Karein",
    ),
    "common.none": (
        "None",
        "कोई नहीं",
        "काहीही नाही",
        "Koi nahi",
    ),
    "dashboard.executive.sectionUnavailable": (
        "This section isn't available for your role, or its data couldn't be loaded.",
        "यह section आपकी role के लिए उपलब्ध नहीं है, या इसका data load नहीं हो सका।",
        "हा section तुमच्या role साठी उपलब्ध नाही, किंवा त्याचा data load होऊ शकला नाही.",
        "Ye section aapki role ke liye available nahi hai, ya iska data load nahi ho paaya.",
    ),
    "dashboard.executive.viewBudget": (
        "View budget breakdown",
        "Budget breakdown देखें",
        "Budget breakdown पहा",
        "Budget breakdown dekhein",
    ),
    "dashboard.executive.viewCompliance": (
        "View compliance status",
        "Compliance स्थिति देखें",
        "Compliance स्थिती पहा",
        "Compliance status dekhein",
    ),
    "dashboard.executive.viewValuation": (
        "View valuation & depreciation",
        "Valuation और depreciation देखें",
        "Valuation आणि depreciation पहा",
        "Valuation & depreciation dekhein",
    ),
    "dashboard.executive.viewWarranties": (
        "View warranty tracking",
        "Warranty tracking देखें",
        "Warranty tracking पहा",
        "Warranty tracking dekhein",
    ),
    "organisation.orgUnitsList.awaitingApprover": (
        "Awaiting another approver",
        "किसी अन्य approver की प्रतीक्षा में",
        "दुसऱ्या approver ची प्रतीक्षेत",
        "Doosre approver ka wait",
    ),
    "organisation.orgUnitsList.awaitingApproverHint": (
        "You created this unit — a different authorised user must approve it.",
        "आपने यह unit बनाई है — किसी अन्य authorised user को ही इसे approve करना होगा।",
        "तुम्ही ही unit तयार केली आहे — दुसऱ्या authorised user लाच ती approve करावी लागेल.",
        "Aapne ye unit banayi hai — kisi doosre authorised user ko hi ise approve karna hoga.",
    ),
    "reports.poWise.assetCount": (
        "Assets",
        "Assets",
        "Assets",
        "Assets",
    ),
    "reports.poWise.col.assetRef": (
        "Asset Ref",
        "Asset Ref",
        "Asset Ref",
        "Asset Ref",
    ),
    "reports.poWise.col.category": (
        "Category",
        "श्रेणी",
        "श्रेणी",
        "Category",
    ),
    "reports.poWise.col.cost": (
        "Acquisition Cost",
        "Acquisition Cost",
        "Acquisition Cost",
        "Acquisition Cost",
    ),
    "reports.poWise.col.custodian": (
        "Current Custodian",
        "वर्तमान Custodian",
        "सद्य Custodian",
        "Current Custodian",
    ),
    "reports.poWise.col.date": (
        "Acquisition Date",
        "Acquisition Date",
        "Acquisition Date",
        "Acquisition Date",
    ),
    "reports.poWise.col.lifecycle": (
        "Lifecycle State",
        "Lifecycle State",
        "Lifecycle State",
        "Lifecycle State",
    ),
    "reports.poWise.col.location": (
        "Location",
        "लोकेशन",
        "लोकेशन",
        "Location",
    ),
    "reports.poWise.col.name": (
        "Name",
        "नाम",
        "नाव",
        "Name",
    ),
    "reports.poWise.loading": (
        "Loading…",
        "लोड हो रहा है…",
        "लोड होत आहे…",
        "Loading ho raha hai…",
    ),
    "reports.poWise.noAssets": (
        "No assets have been recorded against this PO yet.",
        "इस PO के विरुद्ध अभी तक कोई asset दर्ज नहीं किया गया है।",
        "या PO विरुद्ध अद्याप कोणतीही asset नोंदवली गेलेली नाही.",
        "Is PO ke against abhi tak koi asset record nahi hui hai.",
    ),
    "reports.poWise.totalCost": (
        "Total Acquisition Cost",
        "कुल Acquisition Cost",
        "एकूण Acquisition Cost",
        "Total Acquisition Cost",
    ),
    "user.info.field.contactFields": (
        "Direct Mobile / Extension / Desk Location",
        "डायरेक्ट मोबाइल / एक्सटेंशन / डेस्क लोकेशन",
        "डायरेक्ट मोबाइल / एक्सटेंशन / डेस्क लोकेशन",
        "Direct Mobile / Extension / Desk Location",
    ),
    "user.info.field.contactFieldsDesc": (
        "Optional — the only fields you can self-edit; everything else in Personal Information is set by Administration.",
        "वैकल्पिक — ये एकमात्र fields हैं जिन्हें आप स्वयं edit कर सकते हैं; Personal Information में बाकी सब कुछ Administration द्वारा सेट किया जाता है।",
        "पर्यायी — हे एकमेव fields आहेत जे तुम्ही स्वतः edit करू शकता; Personal Information मधील बाकी सर्व काही Administration द्वारे सेट केले जाते.",
        "Optional — ye sirf wahi fields hain jinhe aap khud edit kar sakte hain; Personal Information mein baaki sab kuch Administration set karta hai.",
    ),
    "user.info.field.mfaCode": (
        "6-Digit Authenticator Code",
        "6-अंकीय Authenticator कोड",
        "6-अंकी Authenticator कोड",
        "6-Digit Authenticator Code",
    ),
    "user.info.field.mfaCodeDesc": (
        "Proves you scanned the QR before MFA is committed to your account — nothing is saved on 'Set Up MFA' alone.",
        "यह साबित करता है कि MFA आपके अकाउंट में commit होने से पहले आपने QR स्कैन किया था — केवल 'Set Up MFA' पर क्लिक करने से कुछ भी save नहीं होता।",
        "हे सिद्ध करते की MFA तुमच्या अकाउंटमध्ये commit होण्यापूर्वी तुम्ही QR scan केला होता — फक्त 'Set Up MFA' वर क्लिक केल्याने काहीही save होत नाही.",
        "Ye prove karta hai ki MFA aapke account mein commit hone se pehle aapne QR scan kiya tha — sirf 'Set Up MFA' par click karne se kuch bhi save nahi hota.",
    ),
    "user.info.field.newPassword": (
        "New Password",
        "नया पासवर्ड",
        "नवीन पासवर्ड",
        "New Password",
    ),
    "user.info.field.newPasswordDesc": (
        "Minimum 8 characters, and cannot be identical to your current password.",
        "न्यूनतम 8 characters होने चाहिए, और यह आपके current password के समान नहीं हो सकता।",
        "किमान 8 characters असणे आवश्यक आहे, आणि ते तुमच्या current password सारखे असू शकत नाही.",
        "Minimum 8 characters chahiye, aur ye aapke current password jaisa same nahi ho sakta.",
    ),
    "user.info.step2.caption": (
        "Read-only view of what your active role can do, grouped by module",
        "आपकी active role क्या कर सकती है, इसका read-only दृश्य, module के अनुसार समूहीकृत",
        "तुमची active role काय करू शकते याचे read-only दृश्य, module नुसार गटबद्ध",
        "Aapki active role kya kar sakti hai uska read-only view, module ke hisaab se grouped",
    ),
    "user.info.step3.caption": (
        "Your own recent account audit events — not other users'",
        "आपके अपने अकाउंट के हाल के audit events — अन्य users के नहीं",
        "तुमच्या स्वतःच्या अकाउंटचे अलीकडील audit events — इतर users चे नाहीत",
        "Aapke apne account ke recent audit events — other users ke nahi",
    ),
    "user.info.step3.label": (
        "Security Logs",
        "सुरक्षा लॉग",
        "सुरक्षा लॉग्स",
        "Security Logs",
    ),
    "users-edit.validation.passwordLength": (
        "Password must be at least 8 characters.",
        "Password कम से कम 8 characters का होना चाहिए।",
        "Password किमान 8 characters चा असावा.",
        "Password kam se kam 8 characters ka hona chahiye.",
    ),
    "vendor-contracts.addPo.available": (
        "available",
        "उपलब्ध",
        "उपलब्ध",
        "available",
    ),
    "vendor-contracts.addPo.budgetAvailableNote": (
        "Assets created against this PO will draw from this budget.",
        "इस PO के विरुद्ध बनाए गए assets इसी budget से draw होंगे।",
        "या PO विरुद्ध तयार केलेल्या assets या budget मधून draw होतील.",
        "Is PO ke against banaye gaye assets isi budget se draw honge.",
    ),
    "vendor-contracts.addPo.budgetLine": (
        "Budget Line (optional)",
        "Budget Line (वैकल्पिक)",
        "Budget Line (ऐच्छिक)",
        "Budget Line (optional)",
    ),
    "vendor-contracts.contracts.breadcrumbProcurement": (
        "Procurement",
        "प्रोक्योरमेंट",
        "प्रोक्युरमेंट",
        "Procurement",
    ),
    "vendor-contracts.contracts.noInvoicesHint": (
        "Submit one using the form above.",
        "ऊपर दिए गए form का उपयोग करके एक submit करें।",
        "वरील form वापरून एक submit करा.",
        "Upar diye gaye form ka use karke ek submit karein.",
    ),
    "vendor-contracts.invoice.info.fieldAmount": (
        "Invoice Amount *",
        "Invoice Amount *",
        "Invoice Amount *",
        "Invoice Amount *",
    ),
    "vendor-contracts.invoice.info.fieldAmountDesc": (
        "Pre-filled from the PO total but editable — compared to PO/GRN within 2% to set status.",
        "PO के total से pre-filled होता है लेकिन editable है — status तय करने के लिए PO/GRN से 2% के भीतर तुलना की जाती है।",
        "PO च्या total वरून pre-filled असते पण editable आहे — status ठरवण्यासाठी PO/GRN शी 2% च्या मर्यादेत तुलना केली जाते.",
        "PO ke total se pre-fill hota hai lekin editable hai — status set karne ke liye PO/GRN se 2% ke andar compare kiya jaata hai.",
    ),
    "vendor-contracts.invoice.info.fieldGrn": (
        "Goods Receipt Note",
        "Goods Receipt Note",
        "Goods Receipt Note",
        "Goods Receipt Note",
    ),
    "vendor-contracts.invoice.info.fieldGrnDesc": (
        "Optional — when linked, the GRN amount is also checked against the invoice.",
        "वैकल्पिक — जुड़े होने पर, GRN amount की भी invoice से जांच की जाती है।",
        "पर्यायी — जोडलेले असल्यास, GRN amount ची देखील invoice शी तपासणी केली जाते.",
        "Optional hai — link hone par, GRN amount ko bhi invoice ke against check kiya jaata hai.",
    ),
    "vendor-contracts.invoice.info.fieldStatus": (
        "Status",
        "Status",
        "Status",
        "Status",
    ),
    "vendor-contracts.invoice.info.fieldStatusDesc": (
        "Set by the server (matched / exception / review / pending) — not typed manually.",
        "यह server द्वारा सेट किया जाता है (matched / exception / review / pending) — इसे manually type नहीं किया जाता।",
        "हे server द्वारे सेट केले जाते (matched / exception / review / pending) — ते manually type केले जात नाही.",
        "Ye server dwara set hota hai (matched / exception / review / pending) — manually type nahi kiya jaata.",
    ),
    "vendor-contracts.invoice.info.step2Caption": (
        "Server compares invoice vs PO vs GRN within a 2% tolerance.",
        "server invoice की तुलना PO और GRN से 2% tolerance के भीतर करता है।",
        "server invoice ची तुलना PO आणि GRN शी 2% tolerance च्या मर्यादेत करतो.",
        "Server invoice ko PO aur GRN ke against 2% tolerance ke andar compare karta hai.",
    ),
    "vendor-contracts.invoice.info.step3Caption": (
        "Exceptions are inspected against the attached documents.",
        "Exceptions की जांच attached documents के विरुद्ध की जाती है।",
        "Exceptions ची तपासणी संलग्न documents विरुद्ध केली जाते.",
        "Exceptions ko attached documents ke against inspect kiya jaata hai.",
    ),
    "vendor-contracts.invoice.info.step3Label": (
        "Review",
        "समीक्षा",
        "पुनरावलोकन",
        "Review",
    ),
    "vendor-contracts.invoice.info.step4Caption": (
        "Mark Matched or Exception to close out the check.",
        "जांच बंद करने के लिए इसे Matched या Exception के रूप में mark करें।",
        "तपासणी पूर्ण करण्यासाठी ती Matched किंवा Exception म्हणून mark करा.",
        "Check close karne ke liye ise Matched ya Exception mark karein.",
    ),
    "vendor-contracts.invoice.info.step4Label": (
        "Verify",
        "सत्यापित करें",
        "पडताळणी करा",
        "Verify",
    ),
    "vendor-contracts.poDetails.assetsPurchased": (
        "Assets Purchased",
        "खरीदी गई Assets",
        "खरेदी केलेल्या Assets",
        "Assets Purchased",
    ),
    "vendor-contracts.purchaseOrders.addPo": (
        "PO",
        "PO",
        "PO",
        "PO",
    ),
}


async def seed_for_session(session: AsyncSession) -> tuple[int, int]:
    now = datetime.now(timezone.utc)
    keys_upserted = 0
    values_upserted = 0
    for key, (en, hi, mr, hinglish) in GAP_BATCH5_ADMIN_VENDOR.items():
        key_id = (await session.execute(
            text("SELECT id FROM translation_keys WHERE namespace = :ns AND key = :key"),
            {"ns": NAMESPACE, "key": key},
        )).scalar_one_or_none()
        if key_id:
            await session.execute(
                text("UPDATE translation_keys SET source_text = :src WHERE id = :id"),
                {"src": en, "id": key_id},
            )
        else:
            key_id = str(uuid.uuid4())
            await session.execute(
                text("""INSERT INTO translation_keys (id, namespace, key, source_text, created_at)
                        VALUES (:id, :ns, :key, :src, :now)"""),
                {"id": key_id, "ns": NAMESPACE, "key": key, "src": en, "now": now},
            )
            keys_upserted += 1

        for lang_code, value in (("hi", hi), ("mr", mr), ("hinglish", hinglish)):
            existing = (await session.execute(
                text("SELECT id FROM translation_values WHERE key_id = :kid AND language_code = :lc"),
                {"kid": key_id, "lc": lang_code},
            )).scalar_one_or_none()
            if existing:
                await session.execute(
                    text("UPDATE translation_values SET value = :val, updated_at = :now WHERE id = :id"),
                    {"val": value, "now": now, "id": existing},
                )
            else:
                await session.execute(
                    text("""INSERT INTO translation_values (id, key_id, language_code, value, updated_by, updated_at)
                            VALUES (:id, :kid, :lc, :val, NULL, :now)"""),
                    {"id": str(uuid.uuid4()), "kid": key_id, "lc": lang_code, "val": value, "now": now},
                )
                values_upserted += 1
    await session.commit()
    return keys_upserted, values_upserted


async def main() -> None:
    engine = create_async_engine(settings.DATABASE_URL, echo=False)
    Session = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

    async with Session() as session:
        tenants = (await session.execute(
            text("SELECT id::text AS id, slug FROM public.tenants WHERE status != 'archived'")
        )).fetchall()

    for tenant in tenants:
        schema = f"tenant_{tenant.id.replace('-', '_')}"
        async with Session() as session:
            await session.execute(text(f"SET search_path TO {schema}, public"))
            try:
                k, v = await seed_for_session(session)
                print(f"  seeded {k} new keys, {v} new values: {tenant.slug}")
            except Exception as exc:
                print(f"  SKIPPED {tenant.slug}: {exc}")
                await session.rollback()

    await engine.dispose()
    print(f"Done — {len(tenants)} tenant(s) processed, {len(GAP_BATCH5_ADMIN_VENDOR)} gap-batch5-admin-vendor keys.")


if __name__ == "__main__":
    asyncio.run(main())
