"""Topic -> multi-site research -> original draft using local inference."""
from contextlib import closing
import json
import re
import db
import blog_research


def configured():
    return True


def init():
    with closing(db.connect()) as c, c:
        c.executescript('''
        CREATE TABLE IF NOT EXISTS blog_jobs (
          id INTEGER PRIMARY KEY, topic TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'queued',
          created_at TEXT DEFAULT (datetime('now')), updated_at TEXT DEFAULT (datetime('now')),
          post_id INTEGER REFERENCES posts(id) ON DELETE SET NULL,
          sources_json TEXT NOT NULL DEFAULT '[]', error TEXT);
        CREATE UNIQUE INDEX IF NOT EXISTS one_active_blog_job ON blog_jobs((1))
          WHERE status IN ('queued','researching','writing');
        ''')


def jobs():
    with closing(db.connect()) as c, c:
        c.execute("""UPDATE blog_jobs SET status='failed',error='Generation was interrupted. Submit the topic again.',
                  updated_at=datetime('now') WHERE status IN ('queued','researching','writing')
                  AND updated_at < datetime('now','-15 minutes')""")
        return [dict(r) for r in c.execute('SELECT * FROM blog_jobs ORDER BY id DESC LIMIT 10')]


def enqueue(topic):
    topic = topic.strip()
    if not 5 <= len(topic) <= 300:
        raise ValueError('Enter a topic between 5 and 300 characters.')
    jobs()
    with closing(db.connect()) as c, c:
        try:
            return c.execute('INSERT INTO blog_jobs(topic) VALUES (?)', (topic,)).lastrowid
        except db.sqlite3.IntegrityError:
            raise ValueError('An article is already being generated. Wait for it to finish.') from None


def validate_article(article):
    if not isinstance(article, dict):
        raise ValueError('The scraper returned an invalid article. Please retry.')
    for key, maximum in [('title', 180), ('excerpt', 400), ('content', 25000), ('tag', 60)]:
        value = article.get(key)
        if not isinstance(value, str) or not value.strip() or len(value) > maximum:
            raise ValueError('The scraped article was incomplete. Please retry.')
        if re.search(r'https?://|www\.|<[^>]+>|\]\(', value, re.I):
            raise ValueError('The generated article contained links or HTML. Please retry.')
    if len(article['content'].split()) < 100:
        raise ValueError('Not enough scraped material was found to write a complete article.')


def compose(topic, research):
    """Build a readable editorial digest from scraped facts, without an AI provider."""
    title = topic.strip().rstrip('.!?')
    paragraphs = [
        f'{title}: what current reports say',
        f'This research digest brings together recent reporting about {title}. It is based on accessible articles from multiple automotive publications. Prices, launch plans and specifications can change, so check the latest official announcement before making a purchase decision.',
    ]
    for item in research:
        sentences = re.split(r'(?<=[.!?])\s+', re.sub(r'\s+', ' ', item['text']).strip())
        snippet = ' '.join(sentences[:4]).strip()
        words = snippet.split()
        if len(words) > 95:
            snippet = ' '.join(words[:95]).rstrip(' ,;:') + '…'
        if snippet:
            paragraphs.append(f"{item['title']}\n{snippet}")
    paragraphs.append('How to use this information\nCompare the reported specifications with the manufacturer page, confirm the current ex-showroom price, and consider service access, running costs and your daily route. Reports can describe different variants or different test conditions, so figures should be compared on the same basis.')
    content = '\n\n'.join(paragraphs)
    return {'title': title[:170], 'excerpt': f'Key updates and practical context about {title}, collected from multiple automotive reports.',
            'content': content, 'tag': 'Research digest'}


def run(job_id):
    try:
        with closing(db.connect()) as c, c:
            claimed = c.execute("UPDATE blog_jobs SET status='researching',updated_at=datetime('now') WHERE id=? AND status='queued'", (job_id,))
            if not claimed.rowcount:
                return
            topic = c.execute('SELECT topic FROM blog_jobs WHERE id=?', (job_id,)).fetchone()[0]
        research = blog_research.research(topic)
        sources = [{k: item[k] for k in ('url','title','published','accessed')} for item in research]
        with closing(db.connect()) as c, c:
            c.execute("UPDATE blog_jobs SET status='writing',sources_json=?,updated_at=datetime('now') WHERE id=?", (json.dumps(sources), job_id))
        article = compose(topic, research)
        validate_article(article)
        # Save the draft and finish the job in one transaction: retries cannot create duplicate posts.
        with closing(db.connect()) as c, c:
            if c.execute('SELECT status FROM blog_jobs WHERE id=?', (job_id,)).fetchone()[0] != 'writing':
                return
            slug = db.unique_slug(article['title'])
            post_id = c.execute('''INSERT INTO posts(slug,title,excerpt,content,cover_image,author,tag,published)
                                VALUES (?,?,?,?,'','CarCompare Team',?,0)''',
                                (slug, article['title'], article['excerpt'], article['content'], article['tag'])).lastrowid
            c.execute("UPDATE blog_jobs SET status='complete',post_id=?,updated_at=datetime('now') WHERE id=?", (post_id, job_id))
    except Exception as error:
        message = str(error) if isinstance(error, ValueError) and not isinstance(error, json.JSONDecodeError) else 'Article generation failed. Please retry.'
        with closing(db.connect()) as c, c:
            c.execute("UPDATE blog_jobs SET status='failed',error=?,updated_at=datetime('now') WHERE id=?", (message, job_id))
