from __future__ import annotations

from pathlib import Path
from typing import Any, Dict, List, Optional, Sequence
import sys

from SYS.logger import log

from SYS import pipeline as ctx
from . import _shared as sh

Cmdlet = sh.Cmdlet
CmdletArg = sh.CmdletArg
SharedArgs = sh.SharedArgs
normalize_hash = sh.normalize_hash
parse_cmdlet_args = sh.parse_cmdlet_args
normalize_result_input = sh.normalize_result_input
should_show_help = sh.should_show_help
from Store import Store
from SYS.utils import sha256_file


class Get_Note(Cmdlet):

    def __init__(self) -> None:
        super().__init__(
            name="get-note",
            summary="List notes on a file in a store.",
            usage='get-note -store <store> [-query "hash:<sha256>"]',
            alias=["get-notes",
                   "get_note"],
            arg=[
                SharedArgs.STORE,
                SharedArgs.QUERY,
            ],
            detail=[
                "- Notes are retrieved via the selected store backend.",
                "- Lyrics are stored in a note named 'lyric'.",
            ],
            exec=self.run,
        )
        try:
            SharedArgs.STORE.choices = SharedArgs.get_store_choices(None)
        except Exception:
            pass
        self.register()

    def _resolve_hash(
        self,
        raw_hash: Optional[str],
        raw_path: Optional[str],
        override_hash: Optional[str]
    ) -> Optional[str]:
        resolved = normalize_hash(override_hash
                                  ) if override_hash else normalize_hash(raw_hash)
        if resolved:
            return resolved
        if raw_path:
            try:
                p = Path(str(raw_path))
                stem = p.stem
                if len(stem) == 64 and all(c in "0123456789abcdef"
                                           for c in stem.lower()):
                    return stem.lower()
                if p.exists() and p.is_file():
                    return sha256_file(p)
            except Exception:
                return None
        return None

    def run(self, result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
        if should_show_help(args):
            log(f"Cmdlet: {self.name}\nSummary: {self.summary}\nUsage: {self.usage}")
            return 0

        parsed = parse_cmdlet_args(args, self)
        store_override = parsed.get("store")
        query_hash = sh.parse_single_hash_query(parsed.get("query"))
        if parsed.get("query") and not query_hash:
            log(
                "[get_note] Error: -query must be of the form hash:<sha256>",
                file=sys.stderr
            )
            return 1

        results = normalize_result_input(result)
        if not results:
            if store_override and query_hash:
                results = [{
                    "store": str(store_override),
                    "hash": query_hash
                }]
            else:
                log(
                    '[get_note] Error: Requires piped item(s) or -store and -query "hash:<sha256>"',
                    file=sys.stderr,
                )
                return 1

        store_registry = Store(config)
        any_notes = False
        display_items: List[Dict[str, Any]] = []
        
        # We assume single subject for get-note detail view
        main_res = results[0]
        
        from SYS.result_table import ItemDetailView, extract_item_metadata
        metadata = extract_item_metadata(main_res)

        note_table = (
            ItemDetailView("Notes", item_metadata=metadata)
            .set_table("note")
            .set_value_case("preserve")
            ._perseverance(True)
        )
        note_table.set_source_command("get-note", [])

        for res in results:
            if not isinstance(res, dict):
                continue

            store_name = str(store_override or res.get("store") or "").strip()
            raw_hash = res.get("hash")
            raw_path = res.get("path")

            if not store_name:
                log(
                    "[get_note] Error: Missing -store and item has no store field",
                    file=sys.stderr
                )
                return 1

            resolved_hash = self._resolve_hash(
                raw_hash=str(raw_hash) if raw_hash else None,
                raw_path=str(raw_path) if raw_path else None,
                override_hash=str(query_hash) if query_hash else None,
            )
            if not resolved_hash:
                continue
                
            # Update metadata if we resolved a hash that wasn't in source
            if resolved_hash and not metadata.get("Hash"):
                metadata["Hash"] = resolved_hash
            if store_name and not metadata.get("Store"):
                metadata["Store"] = store_name

            try:
                backend = store_registry[store_name]
            except Exception as exc:
                log(
                    f"[get_note] Error: Unknown store '{store_name}': {exc}",
                    file=sys.stderr
                )
                return 1

            notes = {}
            try:
                notes = backend.get_note(
                    resolved_hash,
                    config=config
                ) or {}
            except Exception:
                notes = {}

            if not notes:
                continue

            any_notes = True
            # Emit each note as its own row so CLI renders a proper note table
            for k in sorted(notes.keys(), key=lambda x: str(x).lower()):
                v = notes.get(k)
                raw_text = str(v or "")
                # Keep payload small for IPC/pipes.
                raw_text = raw_text[:999]
                preview = " ".join(raw_text.replace("\r", "").split("\n"))
                payload: Dict[str, Any] = {
                    "store": store_name,
                    "hash": resolved_hash,
                    "note_name": str(k),
                    "note_text": raw_text,
                    "columns": [
                        ("Name",
                         str(k)),
                        ("Text",
                         preview.strip()),
                    ],
                }
                display_items.append(payload)
                if note_table is not None:
                    row = note_table.add_row()
                    row.add_column("Name", str(k))
                    row.add_column("Text", preview.strip())
                    
                ctx.emit(payload)

        # Always set the table overlay even if empty to show item details
        ctx.set_last_result_table_overlay(note_table, display_items, subject=result)
        
        if not any_notes:
            log("No notes found.")
            
        return 0


CMDLET = Get_Note()
