"""Shared constants, paths, and logging for the autobuilder daemon."""

import datetime as dt
import os
import sys
from pathlib import Path

# ---------------------------------------------------------------------------
# Agent / executor defaults
# ---------------------------------------------------------------------------

DEFAULT_CURSOR_MODEL = "claude-opus-4-7-thinking-high"
DEFAULT_CODEX_MODEL = None
DEFAULT_CLAUDE_MODEL = None    # let `claude` pick its account default
DEFAULT_OPENCODE_MODEL = None  # let `opencode` pick its configured default
CURSOR_CMD = os.environ.get("CURSOR_CMD", "cursor")
CODEX_CMD = os.environ.get("CODEX_CMD", "codex")
CLAUDE_CMD = os.environ.get("CLAUDE_CMD", "claude")
OPENCODE_CMD = os.environ.get("OPENCODE_CMD", "opencode")

# ---------------------------------------------------------------------------
# Timing & limits
# ---------------------------------------------------------------------------

PHASE_HARD_TIMEOUT = 90 * 60   # 90 minutes per phase in chained mode
MAX_ITERATIONS = 999_999_999  # effectively infinite — run 24/7
TASK_NUM_WIDTH = 4

MAX_CONSECUTIVE_CRASHES = 10
MAX_CONSECUTIVE_AGENT_FAILURES = 5
CRASH_BACKOFF_BASE = 30
CRASH_BACKOFF_CAP = 600
AGENT_FAILURE_BACKOFF_BASE = 60
AGENT_FAILURE_BACKOFF_CAP = 1800
PROCESS_MAX_RESTARTS = 50

IDLE_BACKOFF_BASE = 120
IDLE_BACKOFF_CAP = 1800

REVIEW_POLISH_WEIGHT = 0.9
DEFAULT_MAX_TASKS_POLISH = 5
DEFAULT_MAX_TASKS_FEATURE = 3

# ---------------------------------------------------------------------------
# Review strategies — rotated every iteration so different angles are tested
# ---------------------------------------------------------------------------

REVIEW_STRATEGIES = [
    {
        "name": "surface-sweep",
        "focus": (
            "Do a broad surface sweep: navigate every page, check that they load, "
            "look for console errors, run the full test suite. This is the default "
            "first-pass review."
        ),
    },
    {
        "name": "error-handling",
        "focus": (
            "Focus on ERROR HANDLING and RESILIENCE. Test what happens when things "
            "go wrong: visit invalid URLs, submit empty forms, trigger API errors "
            "(e.g. search for nonsense strings, navigate to instruments that don't "
            "exist). Check for missing error states, blank screens, unhandled "
            "exceptions, and poor error messages."
        ),
    },
    {
        "name": "edge-cases",
        "focus": (
            "Focus on EDGE CASES and BOUNDARY CONDITIONS. Test with unusual inputs: "
            "very long strings, special characters, empty datasets, zero results. "
            "Test pagination boundaries, filter combinations, rapid navigation "
            "between pages. Look for race conditions, stale data, and unexpected "
            "empty states."
        ),
    },
    {
        "name": "ux-flows",
        "focus": (
            "Focus on COMPLETE USER JOURNEYS end to end. Pick 3-5 realistic user "
            "scenarios (e.g. 'new user explores the app', 'user researches a stock "
            "and compares it', 'user checks their portfolio'). Walk through each "
            "one step by step. Note friction points, confusing navigation, dead "
            "ends, and missing affordances."
        ),
    },
    {
        "name": "visual-polish",
        "focus": (
            "Focus on VISUAL QUALITY and UI POLISH. Look at spacing, alignment, "
            "typography consistency, color contrast, hover/active states, loading "
            "skeletons, transition smoothness, responsive layout at different "
            "viewport widths. Compare the quality to a professional production app."
        ),
    },
    {
        "name": "performance",
        "focus": (
            "Focus on PERFORMANCE and LOADING. Check for slow page transitions, "
            "large bundle sizes, images without lazy loading, unnecessary re-renders, "
            "API waterfalls, and missing caching. Use the browser network tab and "
            "performance profiling. Look for components that block rendering."
        ),
    },
    {
        "name": "deep-dive",
        "focus": (
            "Focus on the SINGLE MOST COMPLEX FEATURE and stress-test it thoroughly. "
            "Pick the feature with the most code and most user interactions (e.g. "
            "charting, screener, search). Test every button, dropdown, toggle, and "
            "option. Try unlikely combinations. Spend the whole review on this one "
            "area rather than scanning everything broadly."
        ),
    },
    {
        "name": "fresh-eyes",
        "focus": (
            "Pretend you have NEVER SEEN THIS APP before. You are a brand new user "
            "who just landed on it. What is confusing? What's not obvious? Can you "
            "figure out what the app does within 10 seconds? Is the onboarding "
            "clear? Are CTAs visible? Does the information hierarchy make sense? "
            "Focus on first-time user experience."
        ),
    },
    {
        "name": "competitor-comparison",
        "focus": (
            "Open the COMPETITOR (named in constraints/backlog) side by side with our app. "
            "Pick 2-3 major sections and compare them feature by feature: data richness, "
            "interactivity, visual quality, speed, and UX polish. Document every place "
            "where the competitor is clearly superior. Each meaningful gap is a potential "
            "initiative. Do NOT say 'no issues' if the competitor beats us anywhere."
        ),
    },
]

# ---------------------------------------------------------------------------
# Paths — computed relative to this file's location
# ---------------------------------------------------------------------------

SCRIPT_DIR = Path(__file__).resolve().parent          # skills/build-feature/
AUTOBUILDER_DIR = SCRIPT_DIR.parent.parent            # repo root (project is standalone)
REPO_ROOT = AUTOBUILDER_DIR                           # same — no parent repo wrapper
BUILDER_SKILLS_DIR = AUTOBUILDER_DIR / "skills"       # skills/
SKILLS_TARGET = Path(".cursor") / "skills" / "autobuilder"
SKILLS_TARGET_LEGACY = Path(".agents") / "skills"


# ---------------------------------------------------------------------------
# Timestamped logging
# ---------------------------------------------------------------------------

def is_port_open(port: int, host: str = "localhost") -> bool:
    """Return True if *port* is accepting connections on *host*."""
    import socket
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.settimeout(2)
            return s.connect_ex((host, port)) == 0
    except Exception:
        return False


def log(fh, msg: str, also_stdout: bool = True) -> None:
    """Write a timestamped log line to *fh* and optionally to stdout."""
    ts = dt.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    line = f"[{ts}] {msg}\n"
    fh.write(line)
    fh.flush()
    if also_stdout:
        try:
            sys.stdout.write(line)
            sys.stdout.flush()
        except (BrokenPipeError, OSError):
            pass
