import json
import secrets
from datetime import datetime, timedelta

from flask import request, jsonify, render_template, redirect, url_for, flash, session, Response, abort, stream_with_context
from helpers import _db, get_current_user, get_plan_by_id, get_site_settings, send_email, login_required_user, sign_invite_token, verify_invite_token, log_deletion


def _get_team_plan(team):
    if team.get('plan_id'):
        return get_plan_by_id(team['plan_id'])
    return None


def _get_team_effective_plan(team, user_plan):
    team_plan = _get_team_plan(team)
    return team_plan if team_plan else user_plan


def _get_member_permissions(member):
    try:
        if isinstance(member.get('permissions'), str):
            return json.loads(member['permissions'])
        return member.get('permissions') or {}
    except Exception:
        return {"view_files": True, "share": False, "download": True, "delete": False}


def _get_user_team_role(team_id, user_id):
    """
    Returns the active membership row for user_id in team_id, or None.
    Also inherits membership from the parent team: if the team has a parent,
    and the user is an active member of that parent, they are treated as a member
    of this sub-team with their parent-team role.
    """
    with _db() as c:
        row = c.execute(
            "SELECT role, permissions, status FROM team_members WHERE team_id=? AND user_id=? AND status='active' LIMIT 1",
            (team_id, user_id)
        ).fetchone()
        if row:
            return dict(row)
        parent = c.execute("SELECT parent_team_id FROM teams WHERE id=?", (team_id,)).fetchone()
        if parent and parent[0]:
            inherited = c.execute(
                "SELECT role, permissions, status FROM team_members WHERE team_id=? AND user_id=? AND status='active' LIMIT 1",
                (parent[0], user_id)
            ).fetchone()
            if inherited:
                return dict(inherited)
    return None


def _is_team_member_or_owner(team, user_id):
    """True if user owns the team or is an active member (direct or inherited)."""
    if team['owner_user_id'] == user_id:
        return True
    return _get_user_team_role(team['id'], user_id) is not None


def _is_team_admin(team, user_id):
    if team['owner_user_id'] == user_id:
        return True
    role_info = _get_user_team_role(team['id'], user_id)
    return role_info and role_info.get('role') in ('admin', 'owner')


def _get_team_member_user_ids(team_id, owner_user_id):
    """Return set of user_ids considered members of this team (owner + active direct members)."""
    ids = {owner_user_id}
    with _db() as c:
        rows = c.execute(
            "SELECT user_id FROM team_members WHERE team_id=? AND status='active' AND user_id IS NOT NULL",
            (team_id,)
        ).fetchall()
    for r in rows:
        ids.add(r[0])
    return ids


def register_team_routes(app):

    @app.route('/dashboard/teams')
    @app.route('/<lang_code>/dashboard/teams')
    @login_required_user
    def dashboard_teams():
        user = get_current_user()
        plan = get_plan_by_id(user['plan_id'])
        max_teams = int((plan or {}).get('max_teams', 0))

        with _db() as c:
            owned = c.execute(
                "SELECT t.*, p.name AS plan_name FROM teams t LEFT JOIN plans p ON p.id=t.plan_id "
                "WHERE t.owner_user_id=? AND t.parent_team_id IS NULL ORDER BY t.created_at DESC",
                (user['id'],)
            ).fetchall()
            direct_member_of = c.execute(
                """SELECT t.*, p.name AS plan_name, tm.role, tm.status, 'direct' AS membership_type
                   FROM teams t
                   LEFT JOIN plans p ON p.id=t.plan_id
                   JOIN team_members tm ON tm.team_id=t.id
                   WHERE tm.user_id=? AND tm.status='active' AND t.owner_user_id != ?
                   ORDER BY t.created_at DESC""",
                (user['id'], user['id'])
            ).fetchall()
            inherited_member_of = c.execute(
                """SELECT DISTINCT child.*, p.name AS plan_name,
                       tm.role, tm.status, 'inherited' AS membership_type
                   FROM teams child
                   LEFT JOIN plans p ON p.id=child.plan_id
                   JOIN teams parent ON parent.id=child.parent_team_id
                   JOIN team_members tm ON tm.team_id=parent.id
                   WHERE tm.user_id=? AND tm.status='active'
                     AND child.owner_user_id != ?
                     AND child.id NOT IN (
                         SELECT team_id FROM team_members WHERE user_id=? AND status='active'
                     )
                   ORDER BY child.created_at DESC""",
                (user['id'], user['id'], user['id'])
            ).fetchall()

        owned = [dict(r) for r in owned]
        member_of = [dict(r) for r in direct_member_of] + [dict(r) for r in inherited_member_of]
        seen_ids = set()
        unique_member_of = []
        for t in member_of:
            if t['id'] not in seen_ids:
                seen_ids.add(t['id'])
                unique_member_of.append(t)
        member_of = unique_member_of

        for team in owned:
            with _db() as c:
                team['member_count'] = c.execute(
                    "SELECT COUNT(*) FROM team_members WHERE team_id=? AND status='active'", (team['id'],)
                ).fetchone()[0]
                team['sub_team_count'] = c.execute(
                    "SELECT COUNT(*) FROM teams WHERE parent_team_id=?", (team['id'],)
                ).fetchone()[0]

        can_create = max_teams == -1 or (max_teams > 0 and len(owned) < max_teams)

        return render_template('dashboard/teams.html',
                               user=user, plan=plan,
                               owned_teams=owned,
                               member_teams=member_of,
                               can_create=can_create,
                               max_teams=max_teams)

    @app.route('/dashboard/teams/create', methods=['POST'])
    @app.route('/<lang_code>/dashboard/teams/create', methods=['POST'])
    @login_required_user
    def dashboard_team_create():
        user = get_current_user()
        plan = get_plan_by_id(user['plan_id'])
        max_teams = int((plan or {}).get('max_teams', 0))

        name = request.form.get('name', '').strip()
        parent_id = request.form.get('parent_team_id', '').strip()

        if not name:
            flash("Team name is required.", "error")
            return redirect(url_for('dashboard_teams'))

        if parent_id:
            try:
                parent_id = int(parent_id)
            except (ValueError, TypeError):
                parent_id = None
        else:
            parent_id = None

        if parent_id:
            max_sub = int((plan or {}).get('max_sub_teams', 0))
            with _db() as c:
                parent = c.execute("SELECT * FROM teams WHERE id=? AND owner_user_id=?", (parent_id, user['id'])).fetchone()
            if not parent:
                flash("Parent team not found or not owned by you.", "error")
                return redirect(url_for('dashboard_teams'))
            parent = dict(parent)
            if parent.get('parent_team_id') is not None:
                flash("Sub-teams can only be one level deep. Cannot create a sub-team of a sub-team.", "error")
                return redirect(url_for('dashboard_team_detail', team_id=parent_id))
            with _db() as c:
                sub_count = c.execute("SELECT COUNT(*) FROM teams WHERE parent_team_id=?", (parent_id,)).fetchone()[0]
            if max_sub > 0 and sub_count >= max_sub:
                flash(f"Sub-team limit reached ({max_sub}). Upgrade your plan for more.", "error")
                return redirect(url_for('dashboard_teams'))
        else:
            if max_teams == 0:
                flash("Your plan does not include teams. Please upgrade.", "error")
                return redirect(url_for('dashboard_teams'))
            with _db() as c:
                owned_count = c.execute(
                    "SELECT COUNT(*) FROM teams WHERE owner_user_id=? AND parent_team_id IS NULL", (user['id'],)
                ).fetchone()[0]
            if max_teams > 0 and owned_count >= max_teams:
                flash(f"Team limit reached ({max_teams}). Upgrade your plan for more.", "error")
                return redirect(url_for('dashboard_teams'))

        with _db() as c:
            c.execute(
                "INSERT INTO teams (name, owner_user_id, parent_team_id) VALUES (?,?,?)",
                (name, user['id'], parent_id)
            )
            c.commit()
            team_id = c.execute("SELECT last_insert_rowid()").fetchone()[0]

        flash(f"Team '{name}' created.", "success")
        return redirect(url_for('dashboard_team_detail', team_id=team_id))

    @app.route('/dashboard/teams/<int:team_id>')
    @app.route('/<lang_code>/dashboard/teams/<int:team_id>')
    @login_required_user
    def dashboard_team_detail(team_id):
        user = get_current_user()

        with _db() as c:
            team = c.execute("SELECT * FROM teams WHERE id=?", (team_id,)).fetchone()
        if not team:
            abort(404)
        team = dict(team)

        is_owner = team['owner_user_id'] == user['id']
        member_role = _get_user_team_role(team_id, user['id'])
        if not is_owner and not member_role:
            with _db() as c:
                pending = c.execute(
                    "SELECT invite_token FROM team_members WHERE team_id=? AND user_id=? AND status='pending' LIMIT 1",
                    (team_id, user['id'])
                ).fetchone()
            if pending:
                flash("You must accept your team invitation before viewing this workspace.", "info")
                _signed = sign_invite_token(pending['invite_token'])
                return redirect(url_for('dashboard_team_accept', token=_signed))
            flash("You are not a member of this team.", "error")
            return redirect(url_for('dashboard_teams'))

        is_admin = is_owner or (member_role and member_role.get('role') in ('admin',))
        my_permissions = {"view_files": True, "share": True, "download": True, "delete": True} if is_admin else (
            _get_member_permissions(member_role) if member_role else {}
        )

        with _db() as c:
            members = c.execute(
                """SELECT tm.*, u.username, u.email AS user_email
                   FROM team_members tm
                   LEFT JOIN users u ON u.id = tm.user_id
                   WHERE tm.team_id=?
                   ORDER BY tm.status='active' DESC, tm.role DESC, tm.invited_at ASC""",
                (team_id,)
            ).fetchall()
            members = [dict(m) for m in members]

        for m in members:
            m['perms'] = _get_member_permissions(m)

        online_cutoff = (datetime.utcnow() - timedelta(seconds=60)).strftime('%Y-%m-%d %H:%M:%S')
        with _db() as c:
            online_ids = set(
                r[0] for r in c.execute(
                    "SELECT user_id FROM team_presence WHERE team_id=? AND last_seen >= ?",
                    (team_id, online_cutoff)
                ).fetchall()
            )
        for m in members:
            m['is_online'] = m.get('user_id') in online_ids

        page = request.args.get('page', 1, type=int)
        q = request.args.get('q', '').strip()
        per_page = 20
        offset = (page - 1) * per_page

        if is_admin or my_permissions.get('view_files'):
            member_user_ids = list(_get_team_member_user_ids(team_id, team['owner_user_id']))
            if member_user_ids:
                placeholders = ','.join('?' * len(member_user_ids))
                search_clause = ""
                search_params = []
                if q:
                    search_clause = " AND (conv.file_name LIKE ? OR conv.original_file_name LIKE ?)"
                    search_params = [f'%{q}%', f'%{q}%']
                with _db() as c:
                    total_files = c.execute(
                        f"SELECT COUNT(*) FROM conversions conv WHERE conv.user_id IN ({placeholders})"
                        f" AND conv.status != 'deleted'{search_clause}",
                        member_user_ids + search_params
                    ).fetchone()[0]
                    files = c.execute(
                        f"""SELECT conv.*, u.username AS file_owner_name,
                                (SELECT COUNT(*) FROM shared_links sl WHERE sl.job_id=conv.job_id
                                 AND (sl.expires_at IS NULL OR sl.expires_at > datetime('now'))) AS share_count
                            FROM conversions conv
                            JOIN users u ON u.id = conv.user_id
                            WHERE conv.user_id IN ({placeholders}) AND conv.status != 'deleted'{search_clause}
                            ORDER BY conv.id DESC LIMIT ? OFFSET ?""",
                        member_user_ids + search_params + [per_page, offset]
                    ).fetchall()
                files = [dict(f) for f in files]
            else:
                files = []
                total_files = 0
        else:
            files = []
            total_files = 0

        total_pages = max(1, (total_files + per_page - 1) // per_page)

        with _db() as c:
            messages = c.execute(
                """SELECT tm.*, u.username
                   FROM team_messages tm
                   LEFT JOIN users u ON u.id = tm.user_id
                   WHERE tm.team_id=?
                   ORDER BY tm.created_at DESC LIMIT 50""",
                (team_id,)
            ).fetchall()
        messages = list(reversed([dict(m) for m in messages]))

        with _db() as c:
            sub_teams = c.execute(
                "SELECT * FROM teams WHERE parent_team_id=? ORDER BY created_at ASC", (team_id,)
            ).fetchall()
        sub_teams = [dict(s) for s in sub_teams]

        user_plan = get_plan_by_id(user['plan_id'])
        team_plan = _get_team_plan(team)
        effective_plan = team_plan or user_plan
        max_members = int((effective_plan or {}).get('max_members_per_team', 0))

        all_plans = []
        with _db() as c:
            all_plans = [dict(r) for r in c.execute("SELECT id, name, slug, price_monthly FROM plans WHERE is_active=1 ORDER BY price_monthly ASC").fetchall()]

        return render_template('dashboard/team_detail.html',
                               user=user,
                               team=team,
                               is_owner=is_owner,
                               is_admin=is_admin,
                               my_permissions=my_permissions,
                               members=members,
                               files=files,
                               total_files=total_files,
                               page=page,
                               total_pages=total_pages,
                               q=q,
                               messages=messages,
                               sub_teams=sub_teams,
                               user_plan=user_plan,
                               team_plan=team_plan,
                               effective_plan=effective_plan,
                               max_members=max_members,
                               all_plans=all_plans)

    @app.route('/dashboard/teams/<int:team_id>/invite', methods=['POST'])
    @app.route('/<lang_code>/dashboard/teams/<int:team_id>/invite', methods=['POST'])
    @login_required_user
    def dashboard_team_invite(team_id):
        user = get_current_user()
        with _db() as c:
            team = c.execute("SELECT * FROM teams WHERE id=?", (team_id,)).fetchone()
        if not team:
            abort(404)
        team = dict(team)
        if not _is_team_admin(team, user['id']):
            flash("Only team admins can invite members.", "error")
            return redirect(url_for('dashboard_team_detail', team_id=team_id))

        email = request.form.get('email', '').strip().lower()
        role = request.form.get('role', 'member').strip()
        if role not in ('member', 'admin'):
            role = 'member'

        if not email or '@' not in email:
            flash("Valid email address required.", "error")
            return redirect(url_for('dashboard_team_detail', team_id=team_id))

        user_plan = get_plan_by_id(user['plan_id'])
        team_plan = _get_team_plan(team)
        effective_plan = team_plan or user_plan
        max_members = int((effective_plan or {}).get('max_members_per_team', 0))

        with _db() as c:
            current_members = c.execute(
                "SELECT COUNT(*) FROM team_members WHERE team_id=? AND status='active'", (team_id,)
            ).fetchone()[0]

        if max_members > 0 and current_members >= max_members:
            flash(f"Member limit reached ({max_members}). Upgrade your plan for more.", "error")
            return redirect(url_for('dashboard_team_detail', team_id=team_id))

        with _db() as c:
            existing = c.execute(
                "SELECT id, status FROM team_members WHERE team_id=? AND LOWER(invited_email)=?",
                (team_id, email)
            ).fetchone()

        if existing and existing['status'] == 'active':
            flash(f"{email} is already a member.", "error")
            return redirect(url_for('dashboard_team_detail', team_id=team_id))

        with _db() as c:
            invited_user = c.execute("SELECT id FROM users WHERE LOWER(email)=?", (email,)).fetchone()
        invited_uid = invited_user['id'] if invited_user else None

        default_perms = json.dumps({"view_files": True, "share": False, "download": True, "delete": False})
        raw_token = secrets.token_urlsafe(32)
        signed_token = sign_invite_token(raw_token)

        with _db() as c:
            if existing:
                c.execute(
                    "UPDATE team_members SET status='pending', invite_token=?, role=?, invited_by=?, invited_at=datetime('now'), user_id=? "
                    "WHERE team_id=? AND LOWER(invited_email)=?",
                    (raw_token, role, user['id'], invited_uid, team_id, email)
                )
            else:
                c.execute(
                    "INSERT INTO team_members (team_id, user_id, invited_email, role, permissions, status, invite_token, invited_by) "
                    "VALUES (?,?,?,?,?,?,?,?)",
                    (team_id, invited_uid, email, role, default_perms, 'pending', raw_token, user['id'])
                )
            c.commit()

        site = get_site_settings()
        site_url = (site.get('site_url', '') or '').rstrip('/')
        if not site_url:
            site_url = request.host_url.rstrip('/')
        accept_url = f"{site_url}/dashboard/teams/accept/{signed_token}"
        site_name = site.get('site_name', 'OnlineConvert') or 'OnlineConvert'
        team_name = team['name']
        inviter = user.get('username') or user.get('email') or 'Someone'

        subject = f"You've been invited to join the team '{team_name}' on {site_name}"
        html_body = f"""
<div style="font-family:Arial,sans-serif;max-width:560px;margin:0 auto;padding:24px;background:#f9fafb;border-radius:12px">
  <h2 style="color:#1e3a5f">You've been invited to a team workspace!</h2>
  <p style="color:#374151;font-size:15px"><strong>{inviter}</strong> invited you to join the team <strong>{team_name}</strong> on <strong>{site_name}</strong>.</p>
  <a href="{accept_url}" style="display:inline-block;padding:12px 28px;background:#7C3AED;color:#fff;border-radius:8px;text-decoration:none;font-weight:600;font-size:15px;margin:16px 0">
    Accept Invitation
  </a>
  <p style="color:#9ca3af;font-size:12px;margin-top:24px;border-top:1px solid #e5e7eb;padding-top:16px">
    If the button doesn't work, copy this link: <a href="{accept_url}" style="color:#7C3AED">{accept_url}</a>
  </p>
</div>"""
        text_body = f"{inviter} invited you to join team '{team_name}' on {site_name}.\n\nAccept here: {accept_url}"

        send_email(email, subject, html_body, text_body)
        flash(f"Invite sent to {email}.", "success")
        return redirect(url_for('dashboard_team_detail', team_id=team_id))

    @app.route('/dashboard/teams/accept/<path:token>')
    @app.route('/<lang_code>/dashboard/teams/accept/<path:token>')
    def dashboard_team_accept(token, lang_code=None):
        raw_token = verify_invite_token(token)
        if not raw_token:
            flash("Invalid or expired invitation link.", "error")
            return redirect(url_for('dashboard_teams'))

        with _db() as c:
            member = c.execute(
                "SELECT tm.*, t.name AS team_name, t.plan_id AS team_plan_id, t.owner_user_id "
                "FROM team_members tm JOIN teams t ON t.id=tm.team_id "
                "WHERE tm.invite_token=? AND tm.status='pending' LIMIT 1",
                (raw_token,)
            ).fetchone()

        if not member:
            flash("Invalid or expired invitation link.", "error")
            return redirect(url_for('dashboard_teams'))

        member = dict(member)
        invited_email = (member.get('invited_email') or '').strip().lower()

        user = get_current_user()
        if not user:
            next_url = request.url
            if invited_email:
                return redirect(url_for('user_register', next=next_url, email=invited_email))
            return redirect(url_for('user_login', next=next_url))

        user_email = (user.get('email') or '').strip().lower()
        if invited_email and user_email and invited_email != user_email:
            flash(f"This invitation was sent to {invited_email}. Please log in with that account.", "error")
            return redirect(url_for('dashboard_teams'))

        team_id_acc = member['team_id']
        with _db() as c:
            current_count = c.execute(
                "SELECT COUNT(*) FROM team_members WHERE team_id=? AND status='active'", (team_id_acc,)
            ).fetchone()[0]
            team_row = c.execute("SELECT * FROM teams WHERE id=?", (team_id_acc,)).fetchone()
        if team_row:
            team_row = dict(team_row)
            team_plan = get_plan_by_id(team_row.get('plan_id')) if team_row.get('plan_id') else None
            if not team_plan and team_row.get('owner_user_id'):
                with _db() as _oc:
                    owner_row = _oc.execute(
                        "SELECT plan_id FROM users WHERE id=?", (team_row['owner_user_id'],)
                    ).fetchone()
                    owner_plan_id = owner_row['plan_id'] if owner_row else 1
                owner_plan = get_plan_by_id(owner_plan_id)
            else:
                owner_plan = None
            effective_plan = team_plan or owner_plan
            max_members = int((effective_plan or {}).get('max_members_per_team', 0))
            if max_members > 0 and current_count >= max_members:
                flash(f"This team has reached its member limit ({max_members}). Contact the team admin to upgrade the plan.", "error")
                return redirect(url_for('dashboard_teams'))

        with _db() as c:
            c.execute(
                "UPDATE team_members SET status='active', user_id=?, joined_at=datetime('now'), invite_token=NULL "
                "WHERE id=?",
                (user['id'], member['id'])
            )
            c.execute(
                "INSERT INTO team_messages (team_id, user_id, message, message_type) VALUES (?,?,?,?)",
                (team_id_acc, user['id'],
                 f"{user.get('username') or user_email} joined the team.", 'system')
            )
            c.commit()

        flash(f"Welcome to team '{member['team_name']}'!", "success")
        return redirect(url_for('dashboard_team_detail', team_id=team_id_acc))

    @app.route('/dashboard/teams/<int:team_id>/members/<int:member_id>/permissions', methods=['POST'])
    @app.route('/<lang_code>/dashboard/teams/<int:team_id>/members/<int:member_id>/permissions', methods=['POST'])
    @login_required_user
    def dashboard_team_member_permissions(team_id, member_id):
        user = get_current_user()
        with _db() as c:
            team = c.execute("SELECT * FROM teams WHERE id=?", (team_id,)).fetchone()
        if not team:
            abort(404)
        team = dict(team)
        if not _is_team_admin(team, user['id']):
            return jsonify({'ok': False, 'error': 'Forbidden'}), 403

        perms = {
            'view_files': bool(request.form.get('view_files')),
            'share': bool(request.form.get('share')),
            'download': bool(request.form.get('download')),
            'delete': bool(request.form.get('delete')),
        }
        role = request.form.get('role', 'member').strip()
        if role not in ('member', 'admin'):
            role = 'member'

        with _db() as c:
            c.execute(
                "UPDATE team_members SET permissions=?, role=? WHERE id=? AND team_id=?",
                (json.dumps(perms), role, member_id, team_id)
            )
            c.commit()

        flash("Member permissions updated.", "success")
        return redirect(url_for('dashboard_team_detail', team_id=team_id))

    @app.route('/dashboard/teams/<int:team_id>/members/<int:member_id>/remove', methods=['POST'])
    @app.route('/<lang_code>/dashboard/teams/<int:team_id>/members/<int:member_id>/remove', methods=['POST'])
    @login_required_user
    def dashboard_team_member_remove(team_id, member_id):
        user = get_current_user()
        with _db() as c:
            team = c.execute("SELECT * FROM teams WHERE id=?", (team_id,)).fetchone()
        if not team:
            abort(404)
        team = dict(team)
        if not _is_team_admin(team, user['id']):
            flash("Only team admins can remove members.", "error")
            return redirect(url_for('dashboard_team_detail', team_id=team_id))

        with _db() as c:
            # Fetch the member row before deleting so we can cascade-clean their data
            mem_row = c.execute(
                "SELECT user_id, invited_email FROM team_members WHERE id=? AND team_id=?",
                (member_id, team_id)
            ).fetchone()
            c.execute("DELETE FROM team_members WHERE id=? AND team_id=?", (member_id, team_id))
            if mem_row:
                removed_user_id = mem_row['user_id']
                # Prefer the canonical email from users table; fall back to invited_email
                removed_email = mem_row['invited_email'] or ''
                if removed_user_id:
                    u_row = c.execute(
                        "SELECT email FROM users WHERE id=?", (removed_user_id,)
                    ).fetchone()
                    if u_row and u_row['email']:
                        removed_email = u_row['email']
                # Clear their presence record for this team
                if removed_user_id:
                    c.execute(
                        "DELETE FROM team_presence WHERE team_id=? AND user_id=?",
                        (team_id, removed_user_id)
                    )
                # Collect job_ids belonging to current team members (owner + active members)
                member_uid_rows = c.execute(
                    "SELECT user_id FROM team_members WHERE team_id=? AND status='active' AND user_id IS NOT NULL",
                    (team_id,)
                ).fetchall()
                team_member_uids = [team['owner_user_id']] + [r['user_id'] for r in member_uid_rows]
                if team_member_uids:
                    ph = ','.join('?' * len(team_member_uids))
                    job_rows = c.execute(
                        f"SELECT job_id FROM conversions WHERE user_id IN ({ph}) AND job_id IS NOT NULL",
                        team_member_uids
                    ).fetchall()
                    team_job_ids = [r['job_id'] for r in job_rows]
                    # Revoke any access grants held by the removed member for team jobs
                    for _jid in team_job_ids:
                        if removed_user_id:
                            c.execute(
                                "DELETE FROM job_access_grants WHERE job_id=? AND granted_to_user_id=?",
                                (_jid, removed_user_id)
                            )
                        if removed_email:
                            c.execute(
                                "DELETE FROM job_access_grants WHERE job_id=? AND granted_to_email=?",
                                (_jid, removed_email)
                            )
            c.commit()
        if mem_row:
            _removed_name = removed_email or str(member_id)
            log_deletion('team_member', entity_id=member_id, entity_name=_removed_name,
                         actor_user_id=user['id'], actor_username=user.get('username', ''),
                         actor_role='team_member', team_id=team_id, team_name=team.get('name', ''),
                         extra_meta={'removed_user_id': removed_user_id, 'team_id': team_id},
                         entity_owner_user_id=removed_user_id)
        flash("Member removed.", "success")
        return redirect(url_for('dashboard_team_detail', team_id=team_id))

    @app.route('/dashboard/teams/<int:team_id>/members/<int:member_id>/cancel-invite', methods=['POST'])
    @app.route('/<lang_code>/dashboard/teams/<int:team_id>/members/<int:member_id>/cancel-invite', methods=['POST'])
    @login_required_user
    def dashboard_team_cancel_invite(team_id, member_id):
        user = get_current_user()
        with _db() as c:
            team = c.execute("SELECT * FROM teams WHERE id=?", (team_id,)).fetchone()
        if not team:
            abort(404)
        team = dict(team)
        if not _is_team_admin(team, user['id']):
            flash("Only team admins can cancel invites.", "error")
            return redirect(url_for('dashboard_team_detail', team_id=team_id))

        with _db() as c:
            c.execute("DELETE FROM team_members WHERE id=? AND team_id=? AND status='pending'", (member_id, team_id))
            c.commit()
        flash("Invitation cancelled.", "success")
        return redirect(url_for('dashboard_team_detail', team_id=team_id))

    @app.route('/dashboard/teams/<int:team_id>/files/<job_id>/transfer', methods=['POST'])
    @app.route('/<lang_code>/dashboard/teams/<int:team_id>/files/<job_id>/transfer', methods=['POST'])
    @login_required_user
    def dashboard_team_file_transfer(team_id, job_id):
        user = get_current_user()
        with _db() as c:
            team = c.execute("SELECT * FROM teams WHERE id=?", (team_id,)).fetchone()
        if not team:
            abort(404)
        team = dict(team)

        team_member_ids = _get_team_member_user_ids(team_id, team['owner_user_id'])

        with _db() as c:
            conv = c.execute("SELECT user_id, file_name FROM conversions WHERE job_id=? LIMIT 1", (job_id,)).fetchone()

        if not conv:
            flash("File not found.", "error")
            return redirect(url_for('dashboard_team_detail', team_id=team_id))

        conv = dict(conv)
        old_owner_id = conv['user_id']
        file_name = conv['file_name'] or job_id

        is_file_owner = (old_owner_id == user['id'])
        is_admin = _is_team_admin(team, user['id'])

        if not is_admin and not is_file_owner:
            flash("Only team admins or the file owner can transfer this file.", "error")
            return redirect(url_for('dashboard_team_detail', team_id=team_id))

        if old_owner_id not in team_member_ids:
            flash("File does not belong to a team member.", "error")
            return redirect(url_for('dashboard_team_detail', team_id=team_id))

        new_owner_id = request.form.get('new_owner_user_id', '').strip()
        try:
            new_owner_id = int(new_owner_id)
        except (ValueError, TypeError):
            flash("Invalid target user.", "error")
            return redirect(url_for('dashboard_team_detail', team_id=team_id))

        if new_owner_id not in team_member_ids:
            flash("Target user is not a member of this team.", "error")
            return redirect(url_for('dashboard_team_detail', team_id=team_id))

        with _db() as c:
            c.execute("UPDATE conversions SET user_id=? WHERE job_id=?", (new_owner_id, job_id))
            old_user = c.execute("SELECT username FROM users WHERE id=?", (old_owner_id,)).fetchone()
            new_user = c.execute("SELECT username FROM users WHERE id=?", (new_owner_id,)).fetchone()
            old_name = old_user['username'] if old_user else str(old_owner_id)
            new_name = new_user['username'] if new_user else str(new_owner_id)
            c.execute(
                "INSERT INTO team_messages (team_id, user_id, message, message_type) VALUES (?,?,?,?)",
                (team_id, user['id'],
                 f"File '{file_name}' transferred from {old_name} to {new_name}.",
                 'system')
            )
            c.commit()

        flash(f"File transferred to {new_name}.", "success")
        return redirect(url_for('dashboard_team_detail', team_id=team_id))

    @app.route('/dashboard/teams/<int:team_id>/chat', methods=['POST'])
    @app.route('/<lang_code>/dashboard/teams/<int:team_id>/chat', methods=['POST'])
    @login_required_user
    def dashboard_team_chat(team_id):
        user = get_current_user()
        with _db() as c:
            team = c.execute("SELECT * FROM teams WHERE id=?", (team_id,)).fetchone()
        if not team:
            abort(404)
        team = dict(team)
        is_owner = team['owner_user_id'] == user['id']
        member_role = _get_user_team_role(team_id, user['id'])
        if not is_owner and not member_role:
            return jsonify({'ok': False, 'error': 'Forbidden'}), 403

        if request.is_json:
            message = (request.get_json(silent=True) or {}).get('message', '')
        else:
            message = request.form.get('message', '')
        message = str(message).strip()[:1000]
        if not message:
            return jsonify({'ok': False, 'error': 'Message cannot be empty'}), 400

        with _db() as c:
            c.execute(
                "INSERT INTO team_messages (team_id, user_id, message, message_type) VALUES (?,?,?,?)",
                (team_id, user['id'], message, 'chat')
            )
            c.commit()
            msg_id = c.execute("SELECT last_insert_rowid()").fetchone()[0]

        return jsonify({'ok': True, 'id': msg_id})

    @app.route('/dashboard/teams/<int:team_id>/ping', methods=['POST'])
    @app.route('/<lang_code>/dashboard/teams/<int:team_id>/ping', methods=['POST'])
    @login_required_user
    def dashboard_team_ping(team_id):
        user = get_current_user()
        with _db() as c:
            team = c.execute("SELECT id, owner_user_id FROM teams WHERE id=?", (team_id,)).fetchone()
        if not team:
            return jsonify({'ok': False, 'error': 'Team not found'}), 404
        team = dict(team)
        if not _is_team_member_or_owner(team, user['id']):
            return jsonify({'ok': False, 'error': 'Forbidden'}), 403
        with _db() as c:
            c.execute(
                "INSERT OR REPLACE INTO team_presence (team_id, user_id, last_seen) VALUES (?,?,datetime('now'))",
                (team_id, user['id'])
            )
            c.commit()
        return jsonify({'ok': True})

    @app.route('/dashboard/teams/<int:team_id>/events')
    @app.route('/<lang_code>/dashboard/teams/<int:team_id>/events')
    @login_required_user
    def dashboard_team_events(team_id):
        user = get_current_user()
        with _db() as c:
            team = c.execute("SELECT * FROM teams WHERE id=?", (team_id,)).fetchone()
        if not team:
            abort(404)
        team = dict(team)
        is_owner = team['owner_user_id'] == user['id']
        member_role = _get_user_team_role(team_id, user['id'])
        if not is_owner and not member_role:
            abort(403)

        def generate():
            last_msg_id = 0
            with _db() as c:
                row = c.execute("SELECT MAX(id) FROM team_messages WHERE team_id=?", (team_id,)).fetchone()
                last_msg_id = row[0] or 0

            yield "data: {\"type\":\"connected\"}\n\n"

            import time
            tick = 0
            while True:
                time.sleep(2)
                tick += 1

                with _db() as c:
                    new_msgs = c.execute(
                        """SELECT tm.id, tm.message, tm.message_type, tm.created_at, u.username
                           FROM team_messages tm
                           LEFT JOIN users u ON u.id=tm.user_id
                           WHERE tm.team_id=? AND tm.id > ?
                           ORDER BY tm.id ASC LIMIT 20""",
                        (team_id, last_msg_id)
                    ).fetchall()

                for msg in new_msgs:
                    msg = dict(msg)
                    last_msg_id = max(last_msg_id, msg['id'])
                    payload = json.dumps({
                        'type': 'message',
                        'id': msg['id'],
                        'message': msg['message'],
                        'message_type': msg['message_type'],
                        'created_at': msg['created_at'],
                        'username': msg.get('username') or 'Unknown',
                    })
                    yield f"data: {payload}\n\n"

                if tick % 10 == 0:
                    online_cutoff = (datetime.utcnow() - timedelta(seconds=60)).strftime('%Y-%m-%d %H:%M:%S')
                    with _db() as c:
                        online = c.execute(
                            "SELECT tp.user_id, u.username FROM team_presence tp "
                            "JOIN users u ON u.id=tp.user_id "
                            "WHERE tp.team_id=? AND tp.last_seen >= ?",
                            (team_id, online_cutoff)
                        ).fetchall()
                    payload = json.dumps({
                        'type': 'presence',
                        'online': [{'user_id': r[0], 'username': r[1]} for r in online]
                    })
                    yield f"data: {payload}\n\n"

                yield ": heartbeat\n\n"

        return Response(
            stream_with_context(generate()),
            mimetype='text/event-stream',
            headers={
                'Cache-Control': 'no-cache',
                'X-Accel-Buffering': 'no',
            }
        )

    @app.route('/dashboard/teams/<int:team_id>/delete', methods=['POST'])
    @app.route('/<lang_code>/dashboard/teams/<int:team_id>/delete', methods=['POST'])
    @login_required_user
    def dashboard_team_delete(team_id):
        user = get_current_user()
        with _db() as c:
            team = c.execute("SELECT * FROM teams WHERE id=? AND owner_user_id=?", (team_id, user['id'])).fetchone()
        if not team:
            flash("Team not found or permission denied.", "error")
            return redirect(url_for('dashboard_teams'))
        with _db() as c:
            # Gather all subteam IDs before deleting anything
            subteam_rows = c.execute(
                "SELECT id FROM teams WHERE parent_team_id=?", (team_id,)
            ).fetchall()
            subteam_ids = [r['id'] for r in subteam_rows]

            # Collect all member user_ids across main team and every subteam
            all_team_ids = [team_id] + subteam_ids
            all_member_uid_rows = []
            for _tid in all_team_ids:
                rows = c.execute(
                    "SELECT user_id FROM team_members WHERE team_id=? AND status='active' AND user_id IS NOT NULL",
                    (_tid,)
                ).fetchall()
                all_member_uid_rows.extend(rows)
            all_member_uids = list({r['user_id'] for r in all_member_uid_rows} | {team['owner_user_id']})

            # Revoke shared links and access grants for all jobs owned by team members
            if all_member_uids:
                ph = ','.join('?' * len(all_member_uids))
                job_rows = c.execute(
                    f"SELECT job_id FROM conversions WHERE user_id IN ({ph}) AND job_id IS NOT NULL",
                    all_member_uids
                ).fetchall()
                for r in job_rows:
                    _jid = r['job_id']
                    c.execute("DELETE FROM shared_links WHERE job_id=?", (_jid,))
                    c.execute("DELETE FROM job_access_grants WHERE job_id=?", (_jid,))

            # Delete subteam data (members, messages, presence) before removing subteam rows
            for _stid in subteam_ids:
                c.execute("DELETE FROM team_members WHERE team_id=?", (_stid,))
                c.execute("DELETE FROM team_messages WHERE team_id=?", (_stid,))
                c.execute("DELETE FROM team_presence WHERE team_id=?", (_stid,))

            # Delete main team data
            c.execute("DELETE FROM team_members WHERE team_id=?", (team_id,))
            c.execute("DELETE FROM team_messages WHERE team_id=?", (team_id,))
            c.execute("DELETE FROM team_presence WHERE team_id=?", (team_id,))

            # Delete subteams then the main team
            c.execute("DELETE FROM teams WHERE parent_team_id=?", (team_id,))
            c.execute("DELETE FROM teams WHERE id=?", (team_id,))
            c.commit()
        log_deletion('team', entity_id=team_id, entity_name=team.get('name', str(team_id)),
                     actor_user_id=user['id'], actor_username=user.get('username', ''),
                     actor_role='user', team_id=team_id, team_name=team.get('name', ''),
                     extra_meta={'subteams_deleted': len(subteam_ids)},
                     entity_owner_user_id=user['id'])
        flash("Team deleted.", "success")
        return redirect(url_for('dashboard_teams'))

    @app.route('/dashboard/teams/<int:team_id>/clear-plan', methods=['POST'])
    @app.route('/<lang_code>/dashboard/teams/<int:team_id>/clear-plan', methods=['POST'])
    @login_required_user
    def dashboard_team_clear_plan(team_id):
        user = get_current_user()
        with _db() as c:
            team = c.execute("SELECT * FROM teams WHERE id=? AND owner_user_id=?", (team_id, user['id'])).fetchone()
        if not team:
            flash("Team not found or permission denied.", "error")
            return redirect(url_for('dashboard_teams'))
        with _db() as c:
            c.execute("UPDATE teams SET plan_id=NULL, stripe_subscription_id=NULL WHERE id=?", (team_id,))
            c.commit()
        flash("Team plan cleared. Using personal plan limits.", "success")
        return redirect(url_for('dashboard_team_detail', team_id=team_id))

    @app.route('/dashboard/teams/<int:team_id>/subscribe/stripe/<int:plan_id>', methods=['POST'])
    @app.route('/<lang_code>/dashboard/teams/<int:team_id>/subscribe/stripe/<int:plan_id>', methods=['POST'])
    @login_required_user
    def dashboard_team_subscribe_stripe(team_id, plan_id):
        user = get_current_user()
        with _db() as c:
            team = c.execute("SELECT * FROM teams WHERE id=? AND owner_user_id=?", (team_id, user['id'])).fetchone()
        if not team:
            flash("Team not found or permission denied.", "error")
            return redirect(url_for('dashboard_teams'))
        team = dict(team)

        from helpers import get_payment_setting
        stripe_secret = get_payment_setting('stripe_secret_key', 'STRIPE_SECRET_KEY')
        if not stripe_secret:
            flash("Stripe payments are not configured. Please contact the site administrator.", "error")
            return redirect(url_for('dashboard_team_detail', team_id=team_id))

        try:
            import stripe as _stripe
            _stripe.api_key = stripe_secret
            plan = get_plan_by_id(plan_id)
            if not plan or not plan.get('is_active'):
                flash("Invalid plan selected.", "error")
                return redirect(url_for('dashboard_team_detail', team_id=team_id))

            price_monthly = float(plan.get('price_monthly') or 0)
            if price_monthly <= 0:
                flash("Cannot subscribe to a free plan. Choose a paid plan for team billing.", "error")
                return redirect(url_for('dashboard_team_detail', team_id=team_id))

            stripe_price_id = plan.get('stripe_price_id', '')
            current_mode = 'test' if stripe_secret.startswith('sk_test_') else 'live'
            stored_mode = plan.get('stripe_key_mode', '')
            if stripe_price_id and stored_mode != current_mode:
                stripe_price_id = ''

            if not stripe_price_id:
                stripe_product_id = plan.get('stripe_product_id', '')
                if not stripe_product_id:
                    product = _stripe.Product.create(
                        name=f"{plan['name']} (Team)",
                        description=f"Team plan: {plan['name']}"
                    )
                    stripe_product_id = product['id']
                    with _db() as _c:
                        _c.execute("UPDATE plans SET stripe_product_id=?, stripe_key_mode=? WHERE id=?",
                                   (stripe_product_id, current_mode, plan_id))
                        _c.commit()
                unit_amount = max(1, round(price_monthly * 100))
                price = _stripe.Price.create(
                    product=stripe_product_id,
                    unit_amount=unit_amount,
                    currency='usd',
                    recurring={'interval': 'month', 'interval_count': 1},
                    nickname=f"{plan['name']} – Monthly (Team)"
                )
                stripe_price_id = price['id']
                with _db() as _c:
                    _c.execute("UPDATE plans SET stripe_price_id=?, stripe_key_mode=? WHERE id=?",
                               (stripe_price_id, current_mode, plan_id))
                    _c.commit()

            customer_kwargs = {}
            if user.get('stripe_customer_id'):
                customer_kwargs['customer'] = user['stripe_customer_id']
            else:
                customer_kwargs['customer_email'] = user.get('email', '')

            success_url = (request.host_url.rstrip('/') +
                           f'/dashboard/teams/{team_id}?team_stripe=success')
            cancel_url = (request.host_url.rstrip('/') +
                          f'/dashboard/teams/{team_id}?team_stripe=cancel')

            metadata = {
                'type': 'team_plan',
                'team_id': str(team_id),
                'plan_id': str(plan_id),
            }
            session_obj = _stripe.checkout.Session.create(
                mode='subscription',
                line_items=[{'price': stripe_price_id, 'quantity': 1}],
                success_url=success_url,
                cancel_url=cancel_url,
                metadata=metadata,
                subscription_data={'metadata': metadata},
                **customer_kwargs
            )
            return redirect(session_obj['url'])

        except Exception as e:
            flash(f"Payment error: {str(e)}", "error")
            return redirect(url_for('dashboard_team_detail', team_id=team_id))

    @app.route('/dashboard/teams/<int:team_id>/subscribe/paypal/<int:plan_id>', methods=['POST'])
    @app.route('/<lang_code>/dashboard/teams/<int:team_id>/subscribe/paypal/<int:plan_id>', methods=['POST'])
    @login_required_user
    def dashboard_team_subscribe_paypal(team_id, plan_id):
        user = get_current_user()
        with _db() as c:
            team = c.execute("SELECT * FROM teams WHERE id=? AND owner_user_id=?", (team_id, user['id'])).fetchone()
        if not team:
            flash("Team not found or permission denied.", "error")
            return redirect(url_for('dashboard_teams'))
        team = dict(team)

        from helpers import get_payment_setting
        paypal_client = get_payment_setting('paypal_client_id', 'PAYPAL_CLIENT_ID')
        paypal_secret = get_payment_setting('paypal_client_secret', 'PAYPAL_CLIENT_SECRET')
        if not paypal_client or not paypal_secret:
            flash("PayPal payments are not configured. Please contact the site administrator.", "error")
            return redirect(url_for('dashboard_team_detail', team_id=team_id))

        try:
            import requests as _req
            plan = get_plan_by_id(plan_id)
            if not plan or not plan.get('is_active'):
                flash("Invalid plan selected.", "error")
                return redirect(url_for('dashboard_team_detail', team_id=team_id))

            price_monthly = float(plan.get('price_monthly') or 0)
            if price_monthly <= 0:
                flash("Cannot subscribe to a free plan. Choose a paid plan for team billing.", "error")
                return redirect(url_for('dashboard_team_detail', team_id=team_id))

            paypal_plan_id = plan.get('paypal_plan_id', '')
            if not paypal_plan_id:
                flash("This plan is not configured for PayPal. Please use Stripe or contact the site administrator.", "error")
                return redirect(url_for('dashboard_team_detail', team_id=team_id))

            token_resp = _req.post(
                'https://api-m.paypal.com/v1/oauth2/token',
                auth=(paypal_client, paypal_secret),
                data={'grant_type': 'client_credentials'}
            )
            access_token = token_resp.json().get('access_token', '')
            custom_id = f"type=team_plan&team_id={team_id}&plan_id={plan_id}&period_months=1"
            return_url = (request.host_url.rstrip('/') + '/paypal/success')
            cancel_url = (request.host_url.rstrip('/') + f'/dashboard/teams/{team_id}')
            sub_resp = _req.post(
                'https://api-m.paypal.com/v1/billing/subscriptions',
                headers={'Authorization': f'Bearer {access_token}', 'Content-Type': 'application/json'},
                json={
                    'plan_id': paypal_plan_id,
                    'custom_id': custom_id,
                    'application_context': {
                        'return_url': return_url,
                        'cancel_url': cancel_url
                    }
                }
            )
            sub_data = sub_resp.json()
            approve_link = next(
                (l['href'] for l in sub_data.get('links', []) if l['rel'] == 'approve'),
                None
            )
            if approve_link:
                return redirect(approve_link, code=303)
            flash("Could not initiate PayPal subscription. Please try again.", "error")
            return redirect(url_for('dashboard_team_detail', team_id=team_id))

        except Exception as e:
            flash(f"PayPal error: {str(e)}", "error")
            return redirect(url_for('dashboard_team_detail', team_id=team_id))
