from flask import Flask, render_template, redirect, url_for, request, flash, jsonify, session, Blueprint
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
import requests
import os
import calendar
import uuid
from datetime import datetime, timedelta
import pytz
from functools import wraps
import secrets
from werkzeug.security import generate_password_hash, check_password_hash
from werkzeug.utils import secure_filename
from flask_migrate import Migrate
from itsdangerous import URLSafeTimedSerializer
from flask_mail import Mail, Message
from apscheduler.schedulers.background import BackgroundScheduler
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.exc import SQLAlchemyError
import logging
from urllib.parse import urlencode
from flask_limiter import Limiter

# -------------------- Config --------------------
LOCAL_TZ = pytz.timezone("Africa/Maseru")

FB_APP_ID = "1211588177399102"
FB_APP_SECRET = "154b3c51109bb4169890d544d141d8f0"
FB_REDIRECT_URI = "https://test-host.co.ls/Econet-Business/facebook/callback" 

WEBHOOK_SEND_FILE = "https://hook.eu2.make.com/utfnnaocu8e6du73i7c2es7qfsxjz2du"
WEBHOOK_POST = "https://hook.eu2.make.com/ohxlktclpc5btf9vtpssxtuubzl3ca8u"
WEBHOOK_SUBSCRIPTION = "https://hook.eu2.make.com/xcgt6zuc2lxcpqp3vlhwpuspqswm77rf"
MAKE_WEBHOOK_URL = "https://hook.eu2.make.com/j64u5rj9rtsuczkrllgydwvsyy8xay2h"
COMMENTS_WEBHOOK_URL = "https://hook.eu2.make.com/dyu3n7og1fck37dq7rom80hf3ayxc8cw"
MESSAGES_WEBHOOK_URL = "https://hook.eu2.make.com/3y0e9u7whcx44vshvgb64t3o4n1oyb0u"


messenger_bp = Blueprint("messenger", __name__)



GRAPH_API_VERSION = "v24.0"
BASE_URL = f"https://graph.facebook.com/{GRAPH_API_VERSION}"

# Ecocash USSD-push endpoint (the one you provided)
ECOCASH_API_URL = "https://dt-externalproxy-1.etl.co.ls/etl/salesagentpay/paymerchant/"

# -------------------- Flask Setup --------------------
app = Flask(__name__)
app.config["SECRET_KEY"] = "supersecretkey"
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///site.db"
app.config["UPLOAD_FOLDER"] = "uploads"


VERIFY_TOKEN = "VERIFY_TOKEN"

# Mail configuration (adjust to your SMTP in production)
app.config.update(
    MAIL_SERVER="localhost",
    MAIL_PORT=25,
    MAIL_USE_TLS=False,
    MAIL_USE_SSL=False,
)



# -------------------- Extensions --------------------
db = SQLAlchemy(app)
migrate = Migrate(app, db)
login_manager = LoginManager(app)
login_manager.login_view = "login"
mail = Mail(app)
s = URLSafeTimedSerializer(app.config["SECRET_KEY"])



# -------------------- Models --------------------
class User(db.Model, UserMixin):
    id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=True)
    password = db.Column(db.String(200), nullable=False)
    ecocash_number = db.Column(db.String(20), nullable=True)
    used_trial = db.Column(db.Boolean, default=False)
    is_admin = db.Column(db.Boolean, default=False)

     # --- Facebook tokens ---
    facebook_page_id = db.Column(db.String(50), nullable=True)
    facebook_page_token = db.Column(db.Text, nullable=True)
    facebook_long_lived_token = db.Column(db.Text, nullable=True)
    facebook_business_id = db.Column(db.String(50), nullable=True)
    facebook_system_user_id = db.Column(db.String(50), nullable=True)
    facebook_system_user_token = db.Column(db.Text, nullable=True)
    facebook_permissions_granted = db.Column(db.Text, nullable=True)  # JSON list
    
    
    
    def set_password(self, password):
        self.password = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password, password)

    @property
    def has_active_subscription(self):
        now = datetime.now(LOCAL_TZ)
        sub = Subscription.query.filter_by(user_id=self.id, status="active").first()
        if sub and sub.end_date:
            sub_end = sub.end_date
            if sub_end.tzinfo is None:
                sub_end = LOCAL_TZ.localize(sub_end)
            return sub_end >= now
        return False
    
    @property
    def active_subscription(self):
        now = datetime.now(LOCAL_TZ)
        sub = Subscription.query.filter_by(
            user_id=self.id,
            status="active"
        ).first()

        if sub and sub.end_date:
            end = sub.end_date
            if end.tzinfo is None:
                end = LOCAL_TZ.localize(end)
            if end >= now:
                return sub

        return None
    
    @property
    def subscription_type(self):
        sub = self.active_subscription
        if sub:
            return sub.type
        elif self.used_trial:
            return "Free Trial"
        return "None"
        
    @property
    def days_left(self):
        """Return remaining days for active subscription (including Free Trial)"""
        now = datetime.now(LOCAL_TZ)
        sub = self.active_subscription
    
        if sub and sub.end_date:
            end = sub.end_date
            if end.tzinfo is None:
                end = LOCAL_TZ.localize(end)
    
            delta = end - now
            return max(delta.days, 0)
    
        return 0
        
    @property
    def is_superuser(self):
        return self.is_admin is True


class Subscription(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.String(36), db.ForeignKey("user.id"), nullable=False)
    type = db.Column(db.String(50))
    start_date = db.Column(db.DateTime, nullable=False)
    end_date = db.Column(db.DateTime, nullable=False)
    status = db.Column(db.String(20), default="active")
    amount_paid = db.Column(db.Float, default=0.0)
    payment_interval = db.Column(db.String(10), default="monthly")  # monthly or yearly

class Document(db.Model):
    id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
    filename = db.Column(db.String(200), nullable=False)
    category = db.Column(db.String(100), nullable=False)
    user_id = db.Column(db.String(36), db.ForeignKey("user.id"), nullable=False)

class Content(db.Model):
    id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
    text = db.Column(db.Text, nullable=False)
    status = db.Column(db.String(20), default="pending")
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    approved_at = db.Column(db.DateTime, nullable=True)
    scheduled_time = db.Column(db.DateTime, nullable=True)
    user_id = db.Column(db.String(36), db.ForeignKey("user.id"), nullable=False)
    user = db.relationship("User", backref=db.backref("contents", lazy=True))

# -------------------- User Loader --------------------
@login_manager.user_loader
def load_user(user_id):
    return db.session.get(User, str(user_id))

# -------------------- Decorators --------------------

def subscription_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):

        # Ensure user is authenticated
        if not current_user.is_authenticated:
            return redirect(url_for("login"))

        # Admins bypass subscription checks
        if current_user.is_admin:
            return f(*args, **kwargs)

        # Active paid subscription
        if current_user.has_active_subscription:
            return f(*args, **kwargs)

        # Active trial
        if current_user.used_trial and current_user.days_left > 0:
            return f(*args, **kwargs)

        flash(
            "You need an active subscription to access this feature.",
            "warning"
        )
        return redirect(url_for("pricing"))

    return decorated_function


# -------------------- Scheduler --------------------
scheduler = BackgroundScheduler()
logger = logging.getLogger(__name__)


def post_scheduled_content():
    with app.app_context():

        now = datetime.now(LOCAL_TZ)

        scheduled_contents = Content.query.filter(
            Content.status == "approved",
            Content.scheduled_time.isnot(None),
            Content.scheduled_time <= now
        ).all()

        for content in scheduled_contents:

            try:

                response = requests.post(
                    WEBHOOK_POST,
                    json={
                        "content": content.text,
                        "username": content.user.username,
                        "user_id": content.user.id,
                    },
                    timeout=10
                )

                response.raise_for_status()

                content.status = "posted"

                logger.info(
                    f"Content {content.id} successfully sent."
                )

            except requests.exceptions.RequestException as e:

                logger.error(
                    f"Failed sending content {content.id}: {e}"
                )

                continue

        try:

            db.session.commit()

        except SQLAlchemyError as e:

            db.session.rollback()

            logger.error(
                f"Database commit failed: {e}"
            )


#--------------Token Refresh Automation---------------------
def refresh_system_user_tokens():
    with app.app_context():

        users = User.query.filter(
            User.facebook_system_user_token.isnot(None)
        ).all()

        for user in users:

            try:

                refresh_url = (
                    f"https://graph.facebook.com/"
                    f"{GRAPH_API_VERSION}/oauth/access_token"
                )

                response = requests.get(
                    refresh_url,
                    params={
                        "grant_type": "fb_exchange_token",
                        "client_id": FB_APP_ID,
                        "client_secret": FB_APP_SECRET,
                        "fb_exchange_token": user.facebook_system_user_token,
                    },
                    timeout=10,
                )

                response.raise_for_status()

                data = response.json()

                if "error" in data:
                    logger.error(
                        f"Facebook error for user {user.id}: "
                        f"{data['error'].get('message')}"
                    )
                    continue

                new_token = data.get("access_token")

                if new_token:
                    user.facebook_system_user_token = new_token

            except requests.exceptions.RequestException as e:
                logger.error(
                    f"Token refresh failed for user {user.id}: {e}"
                )

        try:
            db.session.commit()

        except SQLAlchemyError as e:
            db.session.rollback()
            logger.error(f"Database error while saving refreshed tokens: {e}")


# -------------------- Routes --------------------

# -------------------- Home --------------------
@app.route("/")
def index():
    return render_template("index.html")

# -------------------- About --------------------
@app.route("/about")
def about():
    return render_template("about.html")
    
@app.route("/contact")
def contact():
    return render_template("contact.html")

@app.route("/help")
def help():
    return render_template("help_desk.html")

@app.route("/terms-of-service")
def terms_of_service():
    return render_template("terms_of_service.html")

# Track processed comment/message IDs to prevent duplicates
processed_comments = set()
processed_messages = set()

# -------------------------------
# 1. META WEBHOOK VERIFICATION & EVENTS
# -------------------------------
@app.route('/webhook', methods=['GET', 'POST'])
@app.route("/webhook", methods=["GET", "POST"])
def webhook():

    # -----------------------------
    # Verification request
    # -----------------------------
    if request.method == "GET":

        mode = request.args.get("hub.mode")
        token = request.args.get("hub.verify_token")
        challenge = request.args.get("hub.challenge")

        if mode == "subscribe" and token == VERIFY_TOKEN:

            logger.info("Meta webhook verified successfully.")

            return challenge, 200

        logger.warning("Meta webhook verification failed.")

        return "Verification failed", 403

    # -----------------------------
    # Incoming webhook
    # -----------------------------
    body = request.get_json(silent=True)

    if not body:
        return "Invalid JSON", 400

    if body.get("object") != "page":
        return "Ignored", 200

    logger.info("Webhook event received.")

    for entry in body.get("entry", []):

        try:
            forward_messenger(entry, MESSAGES_WEBHOOK_URL)
        except Exception as e:
            logger.exception(
                f"Messenger forwarding failed: {e}"
            )

        try:
            forward_feed(entry, COMMENTS_WEBHOOK_URL)
        except Exception as e:
            logger.exception(
                f"Comment forwarding failed: {e}"
            )

    return "EVENT_RECEIVED", 200

# -------------------------------
# 2. Forward Messenger events
# -------------------------------
def forward_messenger(entry):

    if "messaging" not in entry:
        return

    for msg in entry["messaging"]:

        message = msg.get("message", {})
        message_id = message.get("mid")

        if not message_id:
            continue

        if message_id in processed_messages:
            continue

        payload = {
            "type": "message",
            "sender": msg.get("sender"),
            "recipient": msg.get("recipient"),
            "timestamp": msg.get("timestamp"),
            "message": message,
            "page_id": entry.get("id")
        }

        try:

            response = requests.post(
                MESSAGES_WEBHOOK_URL,
                json=payload,
                timeout=5
            )

            response.raise_for_status()

            processed_messages.add(message_id)

            logger.info(
                f"Messenger message {message_id} forwarded successfully."
            )

        except requests.exceptions.RequestException as e:

            logger.error(
                f"Failed forwarding Messenger message "
                f"{message_id}: {e}"
            )
            
            
            
# -------------------------------
# 3. Forward Page feed comments
# -------------------------------
def forward_feed(entry):

    if "changes" not in entry:
        return

    for change in entry["changes"]:

        if change.get("field") != "feed":
            continue

        value = change.get("value")

        if not value:
            continue

        if value.get("item") != "comment":
            continue

        comment_id = value.get("comment_id")
        post_id = value.get("post_id")
        parent_id = value.get("parent_id")

        if not all([comment_id, post_id, parent_id]):
            continue

        if parent_id != post_id:
            continue

        if comment_id in processed_comments:
            continue

        payload = {
            "type": "page_comment",
            "page_id": entry.get("id"),
            "timestamp": entry.get("time"),
            "comment": value
        }

        try:

            response = requests.post(
                COMMENTS_WEBHOOK_URL,
                json=payload,
                timeout=5
            )

            response.raise_for_status()

            processed_comments.add(comment_id)

            logger.info(
                f"Comment {comment_id} forwarded successfully."
            )

        except requests.exceptions.RequestException as e:

            logger.error(
                f"Failed forwarding comment "
                f"{comment_id}: {e}"
            )
                    
# -------------------- Facebook OAuth Initiation --------------------
@app.route("/connect-facebook")
@app.route("/connect-facebook")
@login_required
def connect_facebook():

    state = secrets.token_urlsafe(32)

    session["facebook_oauth_state"] = state

    scopes = [
        "public_profile",
        "email",
        "pages_show_list",
        "pages_read_engagement",
        "pages_manage_posts",
        "pages_manage_metadata",
        "pages_manage_engagement",
        "pages_read_user_content",
        "pages_messaging",
        "business_management"
    ]

    params = {
        "client_id": FB_APP_ID,
        "redirect_uri": FB_REDIRECT_URI,
        "state": state,
        "auth_type": "rerequest",
        "scope": ",".join(scopes)
    }

    url = (
        f"https://www.facebook.com/"
        f"{GRAPH_API_VERSION}/dialog/oauth?"
        f"{urlencode(params)}"
    )

    return redirect(url)

@app.template_filter('strftime')
def _jinja2_filter_datetime(date, fmt='%Y-%m-%dT%H:%M'):
    if date is None:
        return ""
    return date.strftime(fmt)



# -------------------- Facebook OAuth Callback --------------------

@app.route("/facebook/callback")
@login_required
def facebook_callback():

    # -----------------------------
    # Verify OAuth state
    # -----------------------------

    returned_state = request.args.get("state")
    saved_state = session.pop("facebook_oauth_state", None)

    if not returned_state or returned_state != saved_state:

        flash(
            "Invalid Facebook authentication request.",
            "danger"
        )

        return redirect(url_for("dashboard"))


    # -----------------------------
    # Get authorization code
    # -----------------------------

    code = request.args.get("code")

    if not code:

        flash(
            "Facebook authorization failed.",
            "danger"
        )

        return redirect(url_for("dashboard"))


    try:

        # -----------------------------
        # Exchange code for token
        # -----------------------------

        token_url = (
            f"https://graph.facebook.com/"
            f"{GRAPH_API_VERSION}/oauth/access_token"
        )


        token_response = requests.get(
            token_url,
            params={
                "client_id": FB_APP_ID,
                "client_secret": FB_APP_SECRET,
                "redirect_uri": FB_REDIRECT_URI,
                "code": code
            },
            timeout=10
        )


        token_response.raise_for_status()

        token_data = token_response.json()


        if "error" in token_data:

            app.logger.error(
                token_data["error"]
            )

            flash(
                "Facebook token exchange failed.",
                "danger"
            )

            return redirect(url_for("dashboard"))


        short_token = token_data.get(
            "access_token"
        )


        if not short_token:

            flash(
                "No Facebook access token received.",
                "danger"
            )

            return redirect(url_for("dashboard"))



        # -----------------------------
        # Exchange for long lived token
        # -----------------------------

        long_token_response = requests.get(

            token_url,

            params={
                "grant_type":
                    "fb_exchange_token",

                "client_id":
                    FB_APP_ID,

                "client_secret":
                    FB_APP_SECRET,

                "fb_exchange_token":
                    short_token
            },

            timeout=10
        )


        long_token_response.raise_for_status()

        long_token_data = (
            long_token_response.json()
        )


        long_token = (
            long_token_data
            .get("access_token")
        )


        if not long_token:

            flash(
                "Could not obtain long-lived token.",
                "danger"
            )

            return redirect(url_for("dashboard"))



        # -----------------------------
        # Get Facebook pages
        # -----------------------------

        pages_response = requests.get(

            f"https://graph.facebook.com/"
            f"{GRAPH_API_VERSION}/me/accounts",

            params={
                "access_token": long_token
            },

            timeout=10
        )


        pages_response.raise_for_status()

        pages_data = pages_response.json()


        pages = pages_data.get(
            "data",
            []
        )


        if not pages:

            flash(
                "No Facebook Pages found.",
                "warning"
            )

            return redirect(url_for("dashboard"))



        # -----------------------------
        # Save tokens
        # -----------------------------

        current_user.facebook_long_lived_token = long_token


        # Keep your existing behaviour:
        # first page selected

        page = pages[0]


        current_user.facebook_page_id = (
            page.get("id")
        )

        current_user.facebook_page_token = (
            page.get("access_token")
        )


        # -----------------------------
        # Subscribe webhook
        # -----------------------------

        subscribe_response = requests.post(

            f"https://graph.facebook.com/"
            f"{GRAPH_API_VERSION}/"
            f"{page.get('id')}/subscribed_apps",

            params={
                "subscribed_fields":
                    "feed,messages",

                "access_token":
                    page.get("access_token")
            },

            timeout=10
        )


        subscribe_response.raise_for_status()



        db.session.commit()



        flash(
            "Facebook connected successfully.",
            "success"
        )


        return redirect(
            url_for("dashboard")
        )



    except requests.exceptions.RequestException as e:


        db.session.rollback()


        app.logger.error(
            f"Facebook connection error: {e}"
        )


        flash(
            "Facebook connection failed.",
            "danger"
        )


        return redirect(
            url_for("dashboard")
        )



    except Exception as e:


        db.session.rollback()


        app.logger.exception(
            f"Unexpected Facebook error: {e}"
        )


        flash(
            "An unexpected error occurred.",
            "danger"
        )


        return redirect(
            url_for("dashboard")
        )
# -------------------- Facebook Page Selection --------------------
@app.route("/facebook/choose-page", methods=["GET"])
@login_required
def choose_facebook_page():
    pages = session.get("fb_pages_list")
    if not pages:
        flash("Facebook Pages list not found. Please reconnect.", "danger")
        return redirect(url_for("dashboard"))
    return render_template("choose_page.html", pages=pages)


@app.route("/facebook/save-page", methods=["POST"])
@login_required
def save_facebook_page():
    page_id = request.form.get("page_id")
    long_lived_user_token = session.get("fb_long_lived_token")

    if not page_id or not long_lived_user_token:
        flash("Invalid page selection. Please reconnect.", "danger")
        return redirect(url_for("dashboard"))

    # Get the page token from the corresponding hidden input
    page_token = request.form.get(f"page_token_{page_id}")
    if not page_token:
        flash("Page token not found. Please reconnect.", "danger")
        return redirect(url_for("dashboard"))

    # Save selected page to database
    current_user.facebook_page_id = page_id
    current_user.facebook_page_token = page_token
    current_user.facebook_long_lived_token = long_lived_user_token
    db.session.commit()

    # Send to Make.com
    payload = {
        "user_id": current_user.id,
        "long_lived_user_token": long_lived_user_token,
        "page_id": page_id,
        "page_access_token": page_token
    }

    try:
        r = requests.post(MAKE_WEBHOOK_URL, json=payload, timeout=10)
        r.raise_for_status()
    except Exception as e:
        print("❌ Failed to send tokens to Make.com:", e)
        flash("Facebook connected, but Make.com webhook failed.", "warning")
        return redirect(url_for("dashboard"))

    # Clear session pages
    session.pop("fb_pages_list", None)
    session.pop("fb_long_lived_token", None)

    flash("Facebook Page connected successfully!", "success")
    return redirect(url_for("dashboard"))

# -------------------- Change Facebook Page --------------------
@app.route("/facebook/change-page")
@login_required
def change_facebook_page():
    # Clear any previous selection from session
    session.pop("fb_pages_list", None)
    session.pop("fb_long_lived_token", None)

    # Redirect to OAuth flow again
    return redirect(url_for("connect_facebook"))

@app.route("/disconnect_facebook_page", methods=["POST"])
@login_required
def disconnect_facebook_page():
    # Remove Facebook info from current user
    current_user.facebook_page_id = None
    current_user.facebook_page_name = None
    # Save to DB
    db.session.commit()
    flash("Facebook page disconnected successfully!", "success")
    return redirect(url_for("dashboard"))
    
# ------------------- Facebook Insights --------------------
#-------------------insights--------------------
def get_page_insights(page_id, page_access_token):
    url = f"https://graph.facebook.com/v24.0/{page_id}/insights"
    params = {
        "metric": "page_follows,page_media_view,page_follows_country,page_follows_city",
        "period": "day",
        "access_token": page_access_token
    }
    try:
        response = requests.get(url, params=params, timeout=10)
        response.raise_for_status()
        return response.json()
    except requests.exceptions.RequestException as e:
        return {"error": {"message": str(e)}}


@app.route("/page-insights")
@login_required
def page_insights_view():
    if not current_user.facebook_page_id or not current_user.facebook_page_token:
        return render_template("insights.html", page_insights={"error": {"message": "No Facebook page connected"}})

    data = get_page_insights(current_user.facebook_page_id, current_user.facebook_page_token)
    return render_template("insights.html", page_insights=data)



# ------------------- API Endpoint for AJAX / JSON --------------------
@app.route("/api/get_page_insights")
@login_required
def get_page_insights_route():
    """
    Return Facebook Page insights as JSON for AJAX calls.
    """
    if not current_user.facebook_page_id or not current_user.facebook_page_token:
        return jsonify({"error": {"message": "No Facebook page connected"}})

    data = get_page_insights(current_user.facebook_page_id, current_user.facebook_page_token)
    return jsonify(data)
    
    
# -------------------- Auth Routes --------------------
@app.route("/signup", methods=["GET", "POST"])
def signup():
    if request.method == "POST":
        username = request.form.get("username")
        email = request.form.get("email")
        password = request.form.get("password")
        facebook_page = request.form.get("facebook_page")  # ✅ read from form

        if not username or not email or not password:
            flash("All fields are required.", "danger")
            return redirect(url_for("signup"))

        if User.query.filter_by(email=email).first():
            flash("Email already exists.", "danger")
            return redirect(url_for("signup"))

        if User.query.filter_by(username=username).first():
            flash("Username already taken.", "danger")
            return redirect(url_for("signup"))

        hashed_password = generate_password_hash(password, method="pbkdf2:sha256")
        user = User(username=username, email=email, password=hashed_password)
        db.session.add(user)
        db.session.commit()

        # --- Send signup data to Make.com ---
        try:
            requests.post(
                "https://hook.eu2.make.com/67oht2141ucgn7sjx4oysaj8ybxmhcan",
                json={
                    "username": user.username,
                    "user_id": user.id,
                    "page_name": facebook_page
                },
                timeout=10
            )
        except Exception as e:
            print("❌ Signup webhook error:", e)

        flash("Account created!", "success")
        return redirect(url_for("login"))

    return render_template("signup.html")

#-------------------- Login --------------------
@limiter.limit("5 per minute")
@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        username = request.form["username"]
        password = request.form["password"]
        user = User.query.filter_by(username=username).first()
        if user and user.check_password(password):
            login_user(user)
            return redirect(url_for("dashboard"))
        else:
            flash("Invalid credentials.", "danger")
            return redirect(url_for("login"))
    return render_template("login.html")

#-------------------- Logout --------------------
@app.route("/logout")
@login_required
def logout():
    logout_user()
    return redirect(url_for("index"))

# -------------------- Password Recovery --------------------
@app.route('/forgot-password', methods=['GET', 'POST'])
def forgot_password():
    if request.method == 'POST':
        email = request.form['email']
        user = User.query.filter_by(email=email).first()
        if user:
            token = s.dumps(email, salt='password-reset')
            link = url_for('reset_password', token=token, _external=True)
            msg = Message("Password Reset Request", recipients=[email])
            msg.body = f"Click here to reset your password: {link}"
            mail.send(msg)
            flash('Password reset link sent to your email.', 'success')
        else:
            flash('Email not found.', 'danger')
        return redirect(url_for('login'))
    return render_template('forgot_password.html')

# -------------------- Reset Password --------------------
@app.route('/reset-password/<token>', methods=['GET', 'POST'])
def reset_password(token):
    try:
        email = s.loads(token, salt='password-reset', max_age=3600)
    except:
        flash('The reset link is invalid or has expired.', 'danger')
        return redirect(url_for('login'))
    
    if request.method == 'POST':
        new_password = request.form['password']
        user = User.query.filter_by(email=email).first()
        if user:
            user.set_password(new_password)
            db.session.commit()
            flash('Password has been reset. You can now log in.', 'success')
            return redirect(url_for('login'))
        flash('User not found.', 'danger')
        return redirect(url_for('login'))

    return render_template('reset_password.html')

# -------------------- Dashboard --------------------
@app.route('/dashboard')
@login_required
def dashboard():
    # Only block certain features, don't block the entire page
    return render_template('dashboard_base.html')



# -------------------------
# Profile Route
# -------------------------
@app.route('/profile')
@login_required
def profile():
    return render_template('profile.html')

# -------------------------
# Settings Route
# -------------------------
@app.route('/settings')
@login_required
def settings():
    return render_template('settings.html')

# -------------------------
# Change Password Route
# -------------------------
@app.route('/change-password', methods=['POST'])
@login_required
def change_password():
    current_password = request.form.get('current_password')
    new_password = request.form.get('new_password')

    # TODO: Implement your password validation logic
    if current_user.check_password(current_password):
        current_user.set_password(new_password)
        flash('Password updated successfully!', 'success')
    else:
        flash('Current password is incorrect.', 'danger')

    return redirect(url_for('settings'))


# -------------------- Subscription Management --------------------
def create_subscription(user, package_name, days, amount, interval="monthly"):
    now = datetime.now(LOCAL_TZ)
    end_date = now + timedelta(days=days)
    sub = Subscription(
        user_id=user.id,
        type=package_name,
        start_date=now,
        end_date=end_date,
        status="active",
        amount_paid=amount,
        payment_interval=interval
    )
    db.session.add(sub)
    db.session.commit()

    # Notify Make.com with package name, number of days, and amount paid (as requested)
    try:
        requests.post(WEBHOOK_SUBSCRIPTION, json={
            "user_id": user.id,
            "subscription_type": package_name,
            "subscription_days": days,
            "subscription_start_date": now.strftime("%Y-%m-%d %H:%M:%S"),
            "subscription_end_date": end_date.strftime("%Y-%m-%d %H:%M:%S"),
            "amount_paid": amount,
            "username": user.username,
            "status": "success",
            "payment_interval": interval
        }, timeout=10)
    except Exception as e:
        print("❌ Failed to notify Make.com:", e)

# -------------------- Ecocash Payment Integration --------------------
def attempt_ecocash_payment(phone_number: str, amount: float, short_code: str = "36174", timeout: int = 15):
    """
    Make a payment request to Ecocash USSD-push endpoint.
    Returns (True, response_text_or_json) on success, (False, response_text_or_json) on failure.
    """
    payload = {
        "msisdn": phone_number,
        "short_code": short_code,
        "amount": amount
    }
    try:
        resp = requests.post(ECOCASH_API_URL, json=payload, timeout=timeout)
        text = None
        try:
            text = resp.json()
        except Exception:
            text = resp.text

        # Heuristic success checks: HTTP 200 and either JSON status or "successfully" in text
        if resp.status_code == 200:
            # if JSON with status field
            if isinstance(text, dict):
                # common field name might be 'status' with 'SUCCESS' or 'success'
                status_val = text.get("status") or text.get("Status") or text.get("message")
                if status_val and ("success" in str(status_val).lower()):
                    return True, text
                # sometimes API returns {"response":"success"} etc.
                flattened = " ".join([str(v) for v in text.values()])
                if "success" in flattened.lower() or "successfully" in flattened.lower():
                    return True, text
            else:
                # text response
                if "success" in str(text).lower() or "successfully" in str(text).lower():
                    return True, text
        # Not a success
        return False, text
    except requests.exceptions.RequestException as e:
        return False, str(e)

# -------------------- Package Routes (each has own route) --------------------

@app.route("/packages")
def packages():
    return render_template("packages.html")


@app.route("/pricing")
@login_required
def pricing():
    return render_template('pricing.html') 
    
# -------------------- Privacy Policy --------------------
@app.route("/privacy-policy")
def privacy_policy():
    return render_template("privacy_policy.html")



# --------------- Package Subscription Routes --------------------
# Startup
@app.route("/subscribe/startup", methods=["POST"])
@login_required
def subscribe_startup():
    phone_number = request.form.get("phone")
    interval = request.form.get("billing_period", "monthly")  # 'monthly' or 'annual'
    if not phone_number:
        flash("Please enter Ecocash number for payment.", "danger")
        return redirect(url_for("dashboard"))

    # prices (local currency values)
    prices = {"monthly": 1, "annual": 1}  # M50 / M500
    days = 30 if interval == "monthly" else 365
    amount = prices.get(interval, prices["monthly"])

    # Call Ecocash API (USSD push)
    success, resp = attempt_ecocash_payment(phone_number=phone_number, amount=amount)
    if success:
        create_subscription(current_user, "Startup", days, amount, interval=interval)
        flash(f"Startup Package subscribed successfully ({interval})!", "success")
    else:
        # Log resp for debugging (server console) and notify user
        print("Ecocash startup payment failed:", resp)
        flash("Payment failed. Please try again or contact support.", "danger")
    return redirect(url_for("pricing"))

# Growth
@app.route("/subscribe/growth", methods=["POST"])
@login_required
def subscribe_growth():
    phone_number = request.form.get("phone")
    interval = request.form.get("billing_period", "monthly")
    if not phone_number:
        flash("Please enter Ecocash number for payment.", "danger")
        return redirect(url_for("dashboard"))

    prices = {"monthly": 1, "annual": 1}  # M100 / M1000
    days = 30 if interval == "monthly" else 365
    amount = prices.get(interval, prices["monthly"])

    success, resp = attempt_ecocash_payment(phone_number=phone_number, amount=amount)
    if success:
        create_subscription(current_user, "Growth", days, amount, interval=interval)
        flash(f"Growth Package subscribed successfully ({interval})!", "success")
    else:
        print("Ecocash growth payment failed:", resp)
        flash("Payment failed. Please try again or contact support.", "danger")
    return redirect(url_for("pricing"))

# Pro
@app.route("/subscribe/pro", methods=["POST"])
@login_required
def subscribe_pro():
    phone_number = request.form.get("phone")
    interval = request.form.get("billing_period", "monthly")
    if not phone_number:
        flash("Please enter Ecocash number for payment.", "danger")
        return redirect(url_for("dashboard"))

    prices = {"monthly": 1, "annual": 1}  # M230 / M2300
    days = 30 if interval == "monthly" else 365
    amount = prices.get(interval, prices["monthly"])

    success, resp = attempt_ecocash_payment(phone_number=phone_number, amount=amount)
    if success:
        create_subscription(current_user, "Pro", days, amount, interval=interval)
        flash(f"Pro Package subscribed successfully ({interval})!", "success")
    else:
        print("Ecocash pro payment failed:", resp)
        flash("Payment failed. Please try again or contact support.", "danger")
    return redirect(url_for("pricing"))

# Free trial (no payment)
@app.route("/subscribe/trial", methods=["GET"])
@app.route("/subscribe/trial", methods=["GET"])
@login_required
def subscribe_trial():

    if current_user.used_trial:

        flash(
            "You have already used your free trial.",
            "danger"
        )

        return redirect(url_for("dashboard"))


    try:

        create_subscription(
            current_user,
            "Free Trial",
            14,
            0,
            interval="trial"
        )

        current_user.used_trial = True

        db.session.commit()


        flash(
            "Free Trial activated! Enjoy 14 days of access.",
            "success"
        )


    except Exception as e:

        db.session.rollback()

        flash(
            "Unable to activate trial.",
            "danger"
        )

        print(
            "Trial activation error:",
            e
        )


    return redirect(url_for("dashboard"))

# -------------------- Content Management --------------------

@app.route("/webhook/new_content", methods=["POST"])
def webhook_new_content():
    data = request.get_json() or request.form
    if not data:
        return jsonify({"status": "error", "message": "No JSON received"}), 400

    user_id = data.get("user_id")
    text = data.get("text")

    if not user_id or not text:
        return jsonify({"status": "error", "message": "Missing user_id or text"}), 400

    user = User.query.filter_by(id=user_id).first()
    if not user:
        return jsonify({"status": "error", "message": "User not found"}), 404

    content = Content(text=text, status="pending", user_id=user.id)
    db.session.add(content)
    db.session.commit()

    return jsonify({"status": "success", "message": "Content added to pending"}), 201
    
# -------------------- File Upload --------------------
@app.route("/upload", methods=["GET", "POST"])
@login_required
@subscription_required
@app.route("/upload", methods=["POST"])
@login_required
def upload_file():

    if "file" not in request.files:

        flash(
            "No file selected.",
            "danger"
        )

        return redirect(
            request.url
        )


    file = request.files["file"]


    if file.filename == "":

        flash(
            "No file selected.",
            "danger"
        )

        return redirect(
            request.url
        )


    allowed_extensions = {
        "png",
        "jpg",
        "jpeg",
        "pdf",
        "doc",
        "docx",
        "txt"
    }


    extension = (
        file.filename
        .rsplit(".", 1)[-1]
        .lower()
    )


    if "." not in file.filename or extension not in allowed_extensions:

        flash(
            "File type not allowed.",
            "danger"
        )

        return redirect(
            request.url
        )


    filename = secure_filename(
        file.filename
    )


    filepath = os.path.join(
        UPLOAD_FOLDER,
        filename
    )


    file.save(filepath)


    flash(
        "File uploaded successfully.",
        "success"
    )


    return redirect(
        url_for("dashboard")
    )
    
@app.route("/edit/<content_id>", methods=["GET", "POST"])
@login_required
@subscription_required
def edit_content(content_id):
    content = Content.query.filter_by(id=content_id, user_id=current_user.id).first_or_404()

    if request.method == "POST":
        new_text = request.form.get("text")
        if new_text:
            content.text = new_text
            db.session.commit()
            flash("Content updated successfully.", "success")
            return redirect(url_for("pending_content"))

        flash("Content cannot be empty.", "error")

    return render_template("edit_content.html", content=content)


# -------------------- Content Views --------------------
@app.route("/pending")
@login_required
@subscription_required
def pending_content():
    # Use local timezone
    now = datetime.now(LOCAL_TZ)
    month = int(request.args.get("month", now.month))
    year = int(request.args.get("year", now.year))

    # Days in the month
    days_in_month = calendar.monthrange(year, month)[1]

    # Previous / next month navigation
    prev_month = month - 1 if month > 1 else 12
    prev_year = year if month > 1 else year - 1
    next_month = month + 1 if month < 12 else 1
    next_year = year if month < 12 else year + 1

    # Fetch pending content for current user
    contents = Content.query.filter_by(status="pending", user_id=current_user.id).all()

    return render_template(
        "pending_content.html",
        contents=contents,
        month=month,
        year=year,
        days_in_month=days_in_month,
        prev_month=prev_month,
        prev_year=prev_year,
        next_month=next_month,
        next_year=next_year,
        month_name=calendar.month_name[month],
        calendar=calendar,  # pass calendar module
        current_day=now.day,
        current_month=now.month,
        current_year=now.year
    )


@app.route("/approved")
@login_required
@subscription_required
def approved_content():
    # Use local timezone
    now = datetime.now(LOCAL_TZ)
    month = request.args.get("month", now.month, type=int)
    year = request.args.get("year", now.year, type=int)

    # Navigation
    prev_month = month - 1 if month > 1 else 12
    prev_year = year if month > 1 else year - 1
    next_month = month + 1 if month < 12 else 1
    next_year = year if month < 12 else year + 1

    # Days in month
    days_in_month = calendar.monthrange(year, month)[1]

    # Current day info
    current_day, current_month, current_year = now.day, now.month, now.year

    # Fetch approved content for current user
    contents = Content.query.filter_by(status="approved", user_id=current_user.id).all()

    return render_template(
        "approved_content.html",
        contents=contents,
        month=month,
        year=year,
        prev_month=prev_month,
        prev_year=prev_year,
        next_month=next_month,
        next_year=next_year,
        days_in_month=days_in_month,
        current_day=current_day,
        current_month=current_month,
        current_year=current_year,
        month_name=calendar.month_name[month],
        calendar=calendar  # pass calendar module
    )


    
@app.route("/posted")
@login_required
@subscription_required
def posted_content():
    contents = Content.query.filter_by(status="posted", user_id=current_user.id).all()
    return render_template("posted_content.html", contents=contents)
    
    
@app.route("/content/schedule/<content_id>", methods=["POST"])
@login_required
@subscription_required
def schedule_content(content_id):
    """
    Standalone route to schedule pending content.
    Only updates scheduled_time; content text is untouched.
    """
    content = Content.query.get_or_404(content_id)

    # Only allow scheduling if content is pending and belongs to current user
    if content.user_id != current_user.id or content.status != "pending":
        flash("You cannot schedule this content.", "danger")
        return redirect(url_for("pending_content"))

    scheduled_time_str = request.form.get("scheduled_time")
    if scheduled_time_str:
        try:
            # Parse naive datetime from input
            dt_naive = datetime.strptime(scheduled_time_str, "%Y-%m-%dT%H:%M")
            # Localize to your server timezone
            content.scheduled_time = LOCAL_TZ.localize(dt_naive)
        except ValueError:
            flash("Invalid date/time format. Use YYYY-MM-DDTHH:MM", "danger")
            return redirect(url_for("pending_content"))

    db.session.commit()
    flash("Content scheduled successfully!", "success")
    return redirect(url_for("pending_content"))


@app.route("/content/approve/<content_id>", methods=["POST"])
@login_required
@subscription_required
def approve_content(content_id):
    content = Content.query.get_or_404(content_id)
    if content.user_id != current_user.id or content.status != "pending":
        flash("You cannot approve this content.", "danger")
        return redirect(url_for("pending_content"))

    content.status = "approved"
    content.approved_at = datetime.now(LOCAL_TZ)
    db.session.commit()
    flash("Content approved successfully!", "success")
    return redirect(url_for("pending_content"))

@app.route("/content/reject/<content_id>", methods=["POST"])
@login_required
@subscription_required
def reject_content(content_id):
    content = Content.query.get_or_404(content_id)
    if content.user_id != current_user.id or content.status != "pending":
        flash("You cannot reject this content.", "danger")
        return redirect(url_for("pending_content"))

    content.status = "rejected"
    db.session.commit()
    flash("Content rejected successfully!", "success")
    return redirect(url_for("pending_content"))

app.register_blueprint(messenger_bp)

scheduler.add_job(post_scheduled_content, "interval", minutes=1)

scheduler.start()

if __name__ == "__main__":
    if not os.path.exists("uploads"):
        os.makedirs("uploads")
    app.run(debug=True)

