import json
import logging
import time

from openai import OpenAI

from prlens.providers.base import BaseReviewer

logger = logging.getLogger(__name__)


class OpenAIReviewer(BaseReviewer):
    MODEL = "gpt-4o"
    MAX_TOKENS = 4096
    MAX_RETRIES = 3

    def __init__(self, api_key: str):
        self.client = OpenAI(api_key=api_key)

    def review(
        self,
        description: str,
        file_name: str,
        diff_patch: str,
        file_content: str,
        guidelines: str,
    ) -> list[dict]:
        raw = self._call_with_retry(description, file_name, diff_patch, file_content, guidelines)
        if raw is None:
            return []
        return self._parse(raw)

    def _call_with_retry(
        self, description: str, file_name: str, diff_patch: str, file_content: str, guidelines: str
    ) -> str | None:
        for attempt in range(self.MAX_RETRIES):
            try:
                response = self.client.chat.completions.create(
                    model=self.MODEL,
                    messages=[
                        {"role": "system", "content": self._build_system_prompt(guidelines)},
                        {"role": "user", "content": self._build_user_prompt(description, file_name, diff_patch, file_content)},
                    ],
                    temperature=0.2,
                    max_tokens=self.MAX_TOKENS,
                )
                return response.choices[0].message.content
            except Exception as e:
                if attempt == self.MAX_RETRIES - 1:
                    logger.error("OpenAI API failed after %d attempts: %s", self.MAX_RETRIES, e)
                    return None
                delay = 2**attempt
                logger.warning(
                    "OpenAI API error (attempt %d/%d): %s. Retrying in %ds...",
                    attempt + 1,
                    self.MAX_RETRIES,
                    e,
                    delay,
                )
                time.sleep(delay)

    def _build_system_prompt(self, guidelines: str) -> str:
        return f"""
You are a strict and precise senior code reviewer.
Review the patch below and identify issues according to the guidelines.

{guidelines}

Rules:
- Focus on added lines (starting with '+') for direct violations.
- Also consider implications of removed lines (starting with '-') — e.g. deleted null checks, removed error handling, dropped permission guards.
- Do not comment on code that already follows best practices.
- Avoid assumptions when context is unclear. Be concise and actionable.
"""

    def _build_user_prompt(
        self, description: str, file_name: str, diff_patch: str, file_content: str
    ) -> str:
        return f"""
You are reviewing file `{file_name}`.

PR description:
{description}

Diff:
{diff_patch}

File content (for context):
{file_content}

Respond with **only** a valid JSON list:
[
  {{
    "line": <line number in the new file (integer)>,
    "severity": "<critical|major|minor|nitpick>",
    "comment": "<concise, actionable comment>"
  }},
  ...
]

Severity guide:
- critical: security vulnerability, data loss risk, crash
- major: logic bug, missing error handling, significant performance issue
- minor: code smell, unclear naming, missing type hint
- nitpick: style preference, minor formatting

If there are no issues, return: []
"""

    def _parse(self, raw: str) -> list[dict]:
        try:
            cleaned = raw.replace("```json", "").replace("```", "").strip()
            return json.loads(cleaned)
        except json.JSONDecodeError:
            logger.warning("Failed to parse OpenAI response as JSON: %s", raw[:200])
            return []
