import logging
from datetime import timedelta

from odoo import api, fields, models, _
from odoo.exceptions import UserError

_logger = logging.getLogger(__name__)


class OtkSeoContentIdea(models.Model):
    _name = 'otk.seo.content.idea'
    _description = 'SEO Content Idea Queue'
    _inherit = ['mail.thread', 'mail.activity.mixin']
    _order = 'scheduled_date, priority desc, create_date'

    name = fields.Char('Title/Topic', required=True, tracking=True,
        help='Working title or topic for this content idea.')

    # === Source Information ===
    keywords = fields.Char('Target Keywords',
        help='Primary keywords for this content.')
    topic_brief = fields.Text('Topic Brief',
        help='Description of what the content should cover.')
    product_id = fields.Many2one('product.template', 'Related Product',
        help='Product to generate content about.')
    reference_ids = fields.Many2many('otk.seo.reference',
        'otk_seo_idea_reference_rel', 'idea_id', 'reference_id',
        string='References',
        help='Reference materials to provide context for content generation.')

    # === Priority & Scheduling ===
    priority = fields.Selection([
        ('0', 'Low'),
        ('1', 'Normal'),
        ('2', 'High'),
        ('3', 'Urgent'),
    ], string='Priority', default='1', index=True, tracking=True,
        help='Priority level for processing. Higher priority ideas are processed first.')

    scheduled_date = fields.Datetime('Scheduled For', tracking=True,
        help='When this content should be generated. Leave empty for manual processing only.')

    # === Generation Settings ===
    content_type = fields.Selection([
        ('blog_post', 'Blog Post'),
        ('product_desc', 'Product Description'),
        ('category_desc', 'Category Description'),
        ('landing_page', 'Landing Page Content'),
        ('social_post', 'Social Media Post'),
    ], string='Content Type', default='blog_post', required=True,
        help='The type of content to generate.')

    template_id = fields.Many2one('otk.seo.template', 'Content Template',
        domain="[('content_type', '=', content_type)]",
        help='Template to use for content generation.')
    brand_voice_id = fields.Many2one('otk.seo.brand.voice', 'Brand Voice',
        default=lambda self: self.env['otk.seo.brand.voice'].get_default_voice(),
        help='Brand voice guidelines to apply.')
    tone = fields.Selection([
        ('professional', 'Professional'),
        ('casual', 'Casual'),
        ('technical', 'Technical'),
        ('persuasive', 'Persuasive'),
        ('conversational', 'Conversational'),
    ], string='Tone', default='professional',
        help='The writing style for generated content.')
    target_word_count = fields.Selection([
        ('short', 'Short (300-500 words)'),
        ('medium', 'Medium (500-1000 words)'),
        ('long', 'Long (1000-2000 words)'),
    ], string='Target Length', default='medium',
        help='Approximate length of generated content.')
    language_id = fields.Many2one('res.lang', 'Language',
        default=lambda self: self._default_language(),
        help='Language for content generation.')

    # === Image Settings ===
    include_images = fields.Boolean('Generate Images', default=True,
        help='Generate images along with the content.')
    image_count = fields.Integer('Image Count', default=1,
        help='Number of images to generate.')
    image_style = fields.Selection([
        ('photorealistic', 'Photorealistic'),
        ('illustration', 'Illustration'),
        ('digital_art', 'Digital Art'),
        ('watercolor', 'Watercolor'),
        ('sketch', 'Sketch'),
        ('3d_render', '3D Render'),
    ], string='Image Style', default='photorealistic',
        help='Visual style for generated images.')

    # === Auto-Publish Settings ===
    auto_create_blog_post = fields.Boolean('Auto-Create Blog Post',
        help='Automatically create a blog post when content is generated and approved.')
    blog_id = fields.Many2one('blog.blog', 'Target Blog',
        help='Blog where the post will be created.')
    auto_publish = fields.Boolean('Auto-Publish Post',
        help='Automatically publish the blog post (make it visible on the website).')

    # === State ===
    state = fields.Selection([
        ('idea', 'Idea'),
        ('scheduled', 'Scheduled'),
        ('generating', 'Generating'),
        ('done', 'Done'),
        ('failed', 'Failed'),
        ('cancelled', 'Cancelled'),
    ], string='Status', default='idea', tracking=True, index=True, copy=False,
        help='Current status of the idea.')

    # === Result ===
    content_id = fields.Many2one('otk.seo.content', 'Generated Content',
        readonly=True, ondelete='set null', copy=False,
        help='The content record created from this idea.')
    blog_post_id = fields.Many2one('blog.post', 'Blog Post',
        related='content_id.blog_post_id', readonly=True,
        help='The blog post created from the generated content.')
    error_message = fields.Text('Error Message', copy=False,
        help='Error details if generation failed.')

    # === Recurring ===
    is_recurring = fields.Boolean('Recurring',
        help='If enabled, a new idea will be automatically created after this one is processed.')
    recurrence_type = fields.Selection([
        ('daily', 'Daily'),
        ('weekly', 'Weekly'),
        ('biweekly', 'Every 2 Weeks'),
        ('monthly', 'Monthly'),
    ], string='Recurrence', default='weekly',
        help='How often a new idea should be created.')
    recurrence_end_date = fields.Date('Recurrence End Date',
        help='Stop creating new occurrences after this date. Leave empty for no end date.')
    parent_idea_id = fields.Many2one('otk.seo.content.idea', 'Parent Idea',
        readonly=True, ondelete='set null', copy=False,
        help='The recurring idea that spawned this occurrence.')

    # === Tracking ===
    generated_at = fields.Datetime('Generated At', readonly=True, copy=False,
        help='When the content was successfully generated.')

    def _default_language(self):
        """Get default language."""
        lang_code = self.env.lang or 'en_US'
        lang = self.env['res.lang']._lang_get(lang_code)
        return lang.id if lang else False

    @api.onchange('scheduled_date')
    def _onchange_scheduled_date(self):
        """Update state based on scheduled date."""
        for record in self:
            if record.scheduled_date and record.state == 'idea':
                record.state = 'scheduled'
            elif not record.scheduled_date and record.state == 'scheduled':
                record.state = 'idea'

    def action_schedule_now(self):
        """Schedule this idea for immediate generation by cron."""
        self.ensure_one()
        if self.state not in ('idea', 'scheduled'):
            raise UserError(_("Can only schedule ideas that are in 'Idea' or 'Scheduled' state."))

        self.write({
            'scheduled_date': fields.Datetime.now(),
            'state': 'scheduled',
        })
        return {
            'type': 'ir.actions.client',
            'tag': 'display_notification',
            'params': {
                'title': _('Scheduled'),
                'message': _('Idea scheduled for immediate processing.'),
                'type': 'success',
                'sticky': False,
                'next': {'type': 'ir.actions.client', 'tag': 'soft_reload'},
            }
        }

    def action_generate_now(self):
        """Generate content immediately (bypass cron schedule)."""
        self.ensure_one()
        if self.state not in ('idea', 'scheduled'):
            raise UserError(_("Can only generate from 'Idea' or 'Scheduled' state."))

        self._process_idea()

        if self.state == 'done' and self.content_id:
            return {
                'type': 'ir.actions.act_window',
                'res_model': 'otk.seo.content',
                'res_id': self.content_id.id,
                'view_mode': 'form',
                'target': 'current',
            }
        return True

    def action_cancel(self):
        """Cancel this idea."""
        for record in self:
            if record.state in ('idea', 'scheduled'):
                record.write({'state': 'cancelled'})

    def action_reset_to_idea(self):
        """Reset failed/cancelled idea back to idea state."""
        for record in self:
            if record.state in ('failed', 'cancelled'):
                record.write({
                    'state': 'idea',
                    'scheduled_date': False,
                    'error_message': False,
                })

    def action_view_content(self):
        """Open the generated content."""
        self.ensure_one()
        if not self.content_id:
            raise UserError(_("No content has been generated yet."))

        return {
            'type': 'ir.actions.act_window',
            'res_model': 'otk.seo.content',
            'res_id': self.content_id.id,
            'view_mode': 'form',
            'target': 'current',
        }

    def _process_idea(self):
        """Submit content generation for this idea (async).

        Creates content record and submits for generation. The content
        will be generated asynchronously by the API. A separate cron job
        monitors idea completion.
        """
        self.ensure_one()

        self.write({
            'state': 'generating',
            'error_message': False,
        })

        try:
            # Build content values
            content_vals = {
                'name': self.name,
                'content_type': self.content_type,
                'source_keywords': self.keywords,
                'source_topic': self.topic_brief,
                'source_product_id': self.product_id.id if self.product_id else False,
                'template_id': self.template_id.id if self.template_id else False,
                'brand_voice_id': self.brand_voice_id.id if self.brand_voice_id else False,
                'tone': self.tone,
                'target_word_count': self.target_word_count,
                'language_id': self.language_id.id if self.language_id else False,
                'requested_image_count': self.image_count if self.include_images else 0,
                'image_style': self.image_style if self.include_images else False,
                'blog_id': self.blog_id.id if self.blog_id else False,
                'reference_ids': [(6, 0, self.reference_ids.ids)] if self.reference_ids else False,
            }

            # Create content record
            content = self.env['otk.seo.content'].create(content_vals)
            self.content_id = content.id

            # Submit content for async generation
            content.action_generate()

            # Create next occurrence for recurring ideas
            self._create_next_occurrence()

            # Content is now in 'generating' state - cron will check completion
            _logger.info(f"Idea {self.id} '{self.name}' submitted for content generation: content {content.id}")

        except Exception as e:
            _logger.exception(f"Idea {self.id} failed: {e}")
            self.write({
                'state': 'failed',
                'error_message': str(e),
            })

    def _create_next_occurrence(self):
        """Create the next occurrence if this is a recurring idea."""
        self.ensure_one()

        if not self.is_recurring:
            return

        # Check if recurrence has ended
        if self.recurrence_end_date and fields.Date.today() >= self.recurrence_end_date:
            _logger.info("Recurring idea %s reached end date %s, no more occurrences",
                         self.id, self.recurrence_end_date)
            return

        # Compute next scheduled date
        base_date = self.scheduled_date or fields.Datetime.now()
        interval_map = {
            'daily': timedelta(days=1),
            'weekly': timedelta(weeks=1),
            'biweekly': timedelta(weeks=2),
            'monthly': timedelta(days=30),
        }
        delta = interval_map.get(self.recurrence_type, timedelta(weeks=1))
        next_date = base_date + delta

        # Check if next date exceeds end date
        if self.recurrence_end_date and next_date.date() > self.recurrence_end_date:
            _logger.info("Next occurrence for idea %s would exceed end date, skipping", self.id)
            return

        # Create next occurrence
        next_idea = self.copy({
            'scheduled_date': next_date,
            'state': 'scheduled',
            'parent_idea_id': self.id,
            'is_recurring': True,
            'recurrence_type': self.recurrence_type,
            'recurrence_end_date': self.recurrence_end_date,
        })

        _logger.info("Created recurring occurrence %s for idea %s, scheduled at %s",
                      next_idea.id, self.id, next_date)

    def _check_content_completion(self):
        """Check if linked content has completed and finalize idea if so."""
        self.ensure_one()

        if not self.content_id:
            return False

        content = self.content_id

        # Check if content has finished generating
        if content.state in ('review', 'images_pending', 'approved', 'published'):
            # Auto-approve and create blog post if configured
            if self.auto_create_blog_post and content.state == 'review':
                content.action_approve()
                content.action_create_blog_post()

                # Auto-publish if enabled
                if self.auto_publish and content.blog_post_id:
                    content.blog_post_id.is_published = True

            self.write({
                'state': 'done',
                'generated_at': fields.Datetime.now(),
            })

            _logger.info(f"Idea {self.id} '{self.name}' completed: content {content.id}")
            return True

        elif content.state == 'draft' and content.error_message:
            # Content generation failed
            self.write({
                'state': 'failed',
                'error_message': content.error_message,
            })
            return True

        # Still generating
        return False

    @api.model
    def cron_process_scheduled_ideas(self):
        """Cron job to process scheduled ideas.

        Runs periodically. Processes ideas whose scheduled_date has passed.
        """
        now = fields.Datetime.now()

        scheduled_ideas = self.search([
            ('state', '=', 'scheduled'),
            ('scheduled_date', '<=', now),
        ], order='priority desc, scheduled_date', limit=5)  # Process up to 5 per run

        _logger.info(f"Processing {len(scheduled_ideas)} scheduled ideas")

        for idea in scheduled_ideas:
            try:
                idea._process_idea()
                # Commit after each to prevent full rollback on error
                self.env.cr.commit()
            except Exception as e:
                _logger.error(f"Error processing scheduled idea {idea.id}: {e}")
                idea.write({
                    'state': 'failed',
                    'error_message': str(e),
                })
                self.env.cr.commit()

        return True

    @api.model
    def cron_check_generating_ideas(self):
        """Cron job to check if generating ideas have completed.

        Runs periodically. Checks ideas in 'generating' state and
        marks them done/failed based on linked content status.
        """
        generating_ideas = self.search([
            ('state', '=', 'generating'),
            ('content_id', '!=', False),
        ], limit=20)

        _logger.info(f"Checking {len(generating_ideas)} generating ideas")

        for idea in generating_ideas:
            try:
                idea._check_content_completion()
            except Exception as e:
                _logger.error(f"Error checking idea {idea.id} completion: {e}")

        return True

    def copy(self, default=None):
        """Reset state when duplicating."""
        default = dict(default or {})
        default.setdefault('state', 'idea')
        default.setdefault('scheduled_date', False)
        default.setdefault('content_id', False)
        default.setdefault('error_message', False)
        default.setdefault('generated_at', False)
        return super().copy(default)
