from flask import Flask, render_template, request, jsonify
from dotenv import load_dotenv

import os
import json
import sqlite3
import requests
import statistics
import logging


load_dotenv()

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
DB_PATH = os.path.join(BASE_DIR, "../jobs.db")

LOG_DIR = os.path.join(BASE_DIR, "logs")
os.makedirs(LOG_DIR, exist_ok=True)

LOG_FILE = os.path.join(LOG_DIR, "app.log")

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
    handlers=[
        logging.FileHandler(LOG_FILE),
        logging.StreamHandler()
    ]
)

logger = logging.getLogger(__name__)

app = Flask(__name__)


def _match_jobs(
    role: str,
    tech_stack: list[str],
    filtered_jobs: list[dict]
) -> dict:
    openrouter_api_url = "https://openrouter.ai/api/v1/chat/completions"
    openrouter_model = "openai/gpt-oss-120b:free"

    api_key = os.getenv("OPENROUTER_API_KEY")

    if not api_key:
        raise ValueError("OPENROUTER_API_KEY missing from environment")

    user_payload = {
        "role": role,
        "tech_stack": ", ".join(tech_stack),
        "filtered_jobs": filtered_jobs
    }

    system_prompt = """
You are a job benchmarking filter.

Select only jobs that are reasonably comparable to the user's role and stack.

Rules:
- Return JSON only.
- Use only job IDs from filtered_jobs.
- Do not invent IDs.
- Prefer jobs with similar role/title.
- Exclude unrelated tracks such as ML, Data, DevOps, SRE, Product, UI/UX unless clearly comparable.
- Do not calculate salary.
- Do not include salary analysis.
- If fewer than 3 jobs are comparable, return fewer than 3.

Output schema:
{
  "selected_job_ids": ["<job_id>"],
  "excluded_jobs": [
    {
      "id": "<job_id>",
      "reason": "<short reason>"
    }
  ]
}
"""

    response = requests.post(
        openrouter_api_url,
        headers={
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
            "HTTP-Referer": "http://localhost:5000",
            "X-Title": "JobSniper Salary Checker"
        },
        json={
            "model": openrouter_model,
            "messages": [
                {
                    "role": "system",
                    "content": system_prompt.strip()
                },
                {
                    "role": "user",
                    "content": json.dumps(user_payload)
                }
            ],
            "temperature": 0,
            "response_format": {
                "type": "json_object"
            }
        },
        timeout=40
    )

    response.raise_for_status()

    data = response.json()
    content = data["choices"][0]["message"]["content"]

    logger.info("LLM OUTPUT: %s", content)

    parsed = json.loads(content)

    valid_ids = {int(job["job_id"]) for job in filtered_jobs}

    selected_ids = [
        int(job_id)
        for job_id in parsed.get("selected_job_ids", [])
        if int(job_id) in valid_ids
    ]

    return {
        "selected_job_ids": selected_ids,
        "excluded_jobs": parsed.get("excluded_jobs", [])
    }


def _calculate_salary_metrics(user_salary: float, matched_jobs: list[dict]) -> dict:
    salary_midpoints = []

    for job in matched_jobs:
        salary_min = job.get("salary_min")
        salary_max = job.get("salary_max")

        if salary_min and salary_max:
            salary_midpoints.append((salary_min + salary_max) / 2)

    if not salary_midpoints:
        return {
            "verdict": "unknown",
            "gap_amount": 0,
            "market_median": 0
        }

    market_median = round(statistics.median(salary_midpoints))
    gap_amount = market_median - user_salary
    gap_percent = ((user_salary - market_median) / market_median) * 100

    if gap_percent <= -15:
        verdict = "underpaid"
    elif gap_percent >= 10:
        verdict = "above_market"
    else:
        verdict = "fair"

    return {
        "verdict": verdict,
        "gap_amount": round(gap_amount),
        "market_median": market_median
    }


def _get_jobs_by_ids(job_ids: list[int]) -> list[dict]:
    if not job_ids:
        return []

    placeholders = ",".join(["?"] * len(job_ids))

    query = f"""
        SELECT
            slug,
            title,
            company,
            salary,
            salary_min,
            salary_max,
            source,
            tags
        FROM jobs
        WHERE id IN ({placeholders})
    """

    conn = sqlite3.connect(DB_PATH)
    conn.row_factory = sqlite3.Row

    try:
        cursor = conn.cursor()
        cursor.execute(query, job_ids)
        rows = cursor.fetchall()

        results = []

        for row in rows:
            job = dict(row)

            try:
                job["tags"] = json.loads(job["tags"])
            except Exception:
                pass

            results.append(job)

        return results

    finally:
        conn.close()


def _get_stack_matched_jobs(tech_stack: list[str]) -> list[dict]:
    clean_stack = [
        skill.strip().lower()
        for skill in tech_stack
        if skill and skill.strip()
    ]

    if not clean_stack:
        return []

    where_conditions = []
    params = []

    for skill in clean_stack:
        where_conditions.append("LOWER(tags) LIKE ?")
        params.append(f"%{skill}%")

    query = f"""
        SELECT
            id AS job_id,
            title,
            company,
            tags,
            source,
            created_at
        FROM jobs
        WHERE salary_usable = 1
        AND (
            {' OR '.join(where_conditions)}
        )
        ORDER BY created_at DESC
    """

    conn = sqlite3.connect(DB_PATH)
    conn.row_factory = sqlite3.Row

    try:
        cursor = conn.cursor()
        cursor.execute(query, params)
        rows = cursor.fetchall()

        results = []

        for row in rows:
            job = dict(row)

            try:
                job["tags"] = json.loads(job["tags"])
            except Exception:
                pass

            results.append(job)

        return results

    finally:
        conn.close()


@app.route("/")
def home():
    return render_template("index.html")


@app.route("/ping")
def ping():
    return jsonify({
        "ok": True
    })


@app.route("/api/check-salary", methods=["POST"])
def check_salary():
    try:
        payload = request.get_json()

        role = payload.get("role")
        salary = payload.get("salary")
        tech_stack = payload.get("tech_stack", [])

        logger.info(
            "Salary check started | role=%s | salary=%s | tech_stack=%s",
            role,
            salary,
            tech_stack
        )

        matched_jobs = _get_stack_matched_jobs(
            tech_stack=tech_stack
        )

        logger.info("Stack matched jobs count=%s", len(matched_jobs))

        matched_result = _match_jobs(
            role=role,
            tech_stack=tech_stack,
            filtered_jobs=matched_jobs
        )

        selected_job_ids = matched_result.get("selected_job_ids") or []

        logger.info("Selected job ids=%s", selected_job_ids)

        selected_jobs = _get_jobs_by_ids(selected_job_ids)

        logger.info("Selected jobs count=%s", len(selected_jobs))

        salary_metrics = _calculate_salary_metrics(
            user_salary=float(salary),
            matched_jobs=selected_jobs
        )

        logger.info("Salary metrics=%s", salary_metrics)

        response_payload = {
            "verdict": salary_metrics["verdict"],
            "gap_amount": salary_metrics["gap_amount"],
            "user_salary": int(float(salary)),
            "market_median": salary_metrics["market_median"],
            "matched_jobs_count": len(selected_jobs),
            "matched_jobs": [
                {
                    "title": job["title"],
                    "company": job["company"],
                    "source": job.get("source", ""),
                    "salary": job.get("salary", "N/A")
                }
                for job in selected_jobs
            ],
            "share_text": (
                f"I may be {salary_metrics['verdict'].replace('_', ' ')} "
                f"by ${abs(salary_metrics['gap_amount']):,} based on verified "
                f"{role} salary data from "
                f"<a target='_blank' href='https://jobsniper.org'>JobSniper</a>."
            )
        }

        logger.info("Salary check completed successfully")

        return jsonify(response_payload)

    except Exception:
        logger.exception("Salary check failed")

        return jsonify({
            "detail": "Unable to check salary right now. Please try again."
        }), 500


if __name__ == "__main__":
    app.run(debug=True)