"""Replace homemade dual-store helpers with fail-closed Cloud-compatible stubs.

Existing generated apps kept platform adapters and a local KV/password directory.
Cutover imports identities, then retires these modules so leftover services cannot
keep writing a second database. Future codegen must not recreate the files.
"""

from __future__ import annotations

from pathlib import Path

from src.domain.codegen.auth_users_adapter import BACKUP_SUFFIX

RETIRED_MARKER = "__P2HS_RETIRED_LOCAL_STORE__"

SHADOW_STORE_RELATIVE = (
    "server/utils/user-db.ts",
    "server/utils/user-db.js",
    "server/utils/records-db.ts",
    "server/utils/records-db.js",
    "server/utils/local-auth.ts",
    "server/utils/local-auth.js",
    "server/utils/data-bridge.ts",
    "server/utils/data-bridge.js",
    "server/utils/app-storage.ts",
    "server/utils/app-storage.js",
    "server/utils/auth-seed.ts",
    "server/utils/auth-seed.js",
)

_STUBS: dict[str, str] = {
    "user-db": f"""\
/* {RETIRED_MARKER} */
import {{ createError }} from 'h3'

function retired(): never {{
  throw createError({{
    statusCode: 503,
    message: 'Local user directory was removed. Use /api/admin/users.',
  }})
}}

export async function ensureUserDatabase() {{}}

export async function upsertDbUserMirror() {{}}

export async function upsertDbUserWithPassword() {{}}

export async function persistUserSnapshot() {{}}

export async function findUserByEmail() {{
  return null
}}

export async function findUserById() {{
  return null
}}

export async function listDbUsers(_event, params = {{}}) {{
  const page = Math.max(1, params.page ?? 1)
  const limit = Math.min(100, Math.max(1, params.limit ?? 20))
  return {{
    users: [],
    pagination: {{ page, limit, total: 0, totalPages: 0 }},
  }}
}}

export async function createDbUser() {{
  return retired()
}}

export async function updateDbUser() {{
  return retired()
}}

export async function deleteDbUser() {{
  return retired()
}}

export async function updateDbUserPassword() {{
  return retired()
}}

export async function recordUserLogin() {{}}

export function toSessionUser(user) {{
  return user
}}

export function publicUserFromDb(user) {{
  return user
}}
""",
    "records-db": f"""\
/* {RETIRED_MARKER} */
import {{ createError }} from 'h3'

function retired(): never {{
  throw createError({{
    statusCode: 503,
    message: 'Local record store was removed. Use /api/records.',
  }})
}}

export async function createDbRecord() {{
  return retired()
}}

export async function updateDbRecord() {{
  return retired()
}}

export async function upsertDbRecord() {{
  return retired()
}}

export async function removeDbRecord() {{}}

export async function replaceDbRecordId() {{
  return retired()
}}

export async function persistEntitySnapshot() {{}}

export async function getDbRecord() {{
  return null
}}

export async function queryDbRecords() {{
  return {{ records: [], total: 0 }}
}}

export async function queryDbRecordsList() {{
  return []
}}

export function requireLocalRecord() {{
  return retired()
}}
""",
    "local-auth": f"""\
/* {RETIRED_MARKER} */
import {{ createError }} from 'h3'
import {{
  getPlatformUserById,
  platformAuthSignIn,
  platformUserToSession,
}} from '~/server/utils/platform-users'
import {{ clearSessionCookie, getSessionUserId, setSessionCookie }} from '~/server/utils/session'

export async function localSignIn(event, input) {{
  const email = input?.email?.trim().toLowerCase()
  const password = input?.password
  if (!email || !password) {{
    throw createError({{ statusCode: 400, message: 'Email and password are required.' }})
  }}
  const platformUser = await platformAuthSignIn(event, email, password)
  if (!platformUser) {{
    throw createError({{ statusCode: 401, message: 'Invalid email or password.' }})
  }}
  if (platformUser.is_active === false) {{
    throw createError({{ statusCode: 403, message: 'This account has been deactivated.' }})
  }}
  await setSessionCookie(event, platformUser.id)
  return {{ success: true, user: platformUserToSession(platformUser) }}
}}

export async function localGetCurrentUser(event) {{
  const userId = await getSessionUserId(event)
  if (!userId) return null
  const user = await getPlatformUserById(event, userId)
  if (!user) return null
  return {{ user: platformUserToSession(user) }}
}}

export async function localSignOut(event) {{
  clearSessionCookie(event)
  return {{ success: true }}
}}
""",
    "data-bridge": f"""\
/* {RETIRED_MARKER} */

export function mergeRecordsById(primary = [], _secondary = []) {{
  return Array.isArray(primary) ? primary : []
}}

export function mergeUsersByEmail(primary = [], _secondary = []) {{
  return Array.isArray(primary) ? primary : []
}}

export async function mirrorPlatformUserToLocal() {{}}

export async function mirrorRecordToLocal() {{}}

export async function syncLocalRecordsToCloud() {{}}

export async function listMergedLocalUsers() {{
  return []
}}

export async function listMergedUsers(_event, cloudUsers = []) {{
  return Array.isArray(cloudUsers) ? cloudUsers : []
}}

export async function listMergedEntityRecords(_event, _entity, cloudRecords = []) {{
  return Array.isArray(cloudRecords) ? cloudRecords : []
}}
""",
    "app-storage": f"""\
/* {RETIRED_MARKER} */

export function getAppStorage() {{
  return {{
    async getItem() {{
      return null
    }},
    async setItem() {{}},
  }}
}}

export async function readAppStorageItem() {{
  return null
}}

export async function writeAppStorageItem() {{}}
""",
    "auth-seed": f"""\
/* {RETIRED_MARKER} */

export async function ensureSeedUsers() {{
  return {{ created: false }}
}}
""",
}


def _stem(path: Path) -> str:
    name = path.name
    if name.endswith(BACKUP_SUFFIX):
        name = name[: -len(BACKUP_SUFFIX)]
    return Path(name).stem


def is_retired_stub(source: str) -> bool:
    return RETIRED_MARKER in source and "useStorage" not in source and "usr_seed_" not in source


def shadow_store_stubs() -> dict[str, str]:
    """Relative path → Cloud-only stub, used by the Actions runner."""
    stubs: dict[str, str] = {}
    for rel in SHADOW_STORE_RELATIVE:
        stem = Path(rel).stem
        if stem in _STUBS:
            stubs[rel.replace("\\", "/")] = _STUBS[stem]
    return stubs


def retire_shadow_store_files(repo_path: str, *, refresh_stubs: bool = False) -> list[str]:
    """Backup homemade dual-store modules and replace them with Cloud-only stubs."""
    root = Path(repo_path)
    retired: list[str] = []
    for rel in SHADOW_STORE_RELATIVE:
        path = root / rel
        if not path.is_file():
            continue
        source = path.read_text(encoding="utf-8-sig")
        stub = _STUBS.get(_stem(path))
        if not stub:
            continue
        already = is_retired_stub(source)
        if already and not refresh_stubs:
            continue
        if not already:
            dest = path.with_name(path.name + BACKUP_SUFFIX)
            if dest.exists():
                dest.unlink()
            dest.write_text(source, encoding="utf-8")
        path.write_text(stub, encoding="utf-8")
        retired.append(rel.replace("\\", "/"))
    return retired


_CLOUD_RECORD_SERVICE = """\
import type { H3Event } from 'h3'
import { createError } from 'h3'
import {
  createRecord,
  deleteRecord,
  extractQueryRecords,
  normalizeCloudRecord,
  queryRecords,
  queryRecordsList,
  updateRecord,
} from '~/server/utils/p2hs'

function requireRecord(result: unknown) {
  const record = normalizeCloudRecord(result)
  if (!record.id) {
    throw createError({ statusCode: 502, message: 'Platform record request failed' })
  }
  return { record }
}

export async function createEntityRecord(
  event: H3Event,
  entity: string,
  data: Record<string, unknown>,
) {
  return requireRecord(await createRecord(event, entity, { data }))
}

export async function updateEntityRecord(
  event: H3Event,
  entity: string,
  id: string,
  data: Record<string, unknown>,
) {
  return requireRecord(await updateRecord(event, entity, id, { data }))
}

export async function deleteEntityRecord(
  event: H3Event,
  entity: string,
  id: string,
): Promise<void> {
  await deleteRecord(event, entity, id)
}

export async function queryEntityRecords(
  event: H3Event,
  entity: string,
  query: Record<string, unknown> = {},
) {
  const result = await queryRecords(event, { entity_slug: entity, ...query })
  const records = extractQueryRecords<Record<string, unknown>>(result)
  const total =
    typeof (result as { total?: unknown })?.total === 'number'
      ? (result as { total: number }).total
      : records.length
  return { records, total }
}

export async function queryEntityRecordsList<T = Record<string, unknown>>(
  event: H3Event,
  entity: string,
  query: Record<string, unknown> = {},
): Promise<T[]> {
  return queryRecordsList<T>(event, { entity_slug: entity, ...query })
}
"""


def install_cloud_only_record_service(repo_path: str) -> list[str]:
    """Replace dual-write record.service with Cloud-only helpers."""
    path = Path(repo_path) / "server/services/record.service.ts"
    if not path.is_file():
        return []
    source = path.read_text(encoding="utf-8-sig")
    if "records-db" not in source and "isCloudConfigured" not in source:
        return []
    dest = path.with_name(path.name + BACKUP_SUFFIX)
    if dest.exists():
        dest.unlink()
    dest.write_text(source, encoding="utf-8")
    path.write_text(_CLOUD_RECORD_SERVICE, encoding="utf-8")
    return ["server/services/record.service.ts"]

